diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000000..4687bc4f44 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015", "react", "stage-0"] +} diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..3c85116424 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,56 @@ +'use strict'; + +const eslintrc = { + extends: ['eslint-config-airbnb'], + env: { + browser: true, + node: true, + mocha: true, + jest: true, + es6: true, + }, + parser: 'babel-eslint', + parserOptions: { + ecmaVersion: 6, + ecmaFeatures: { + jsx: true, + experimentalObjectRestSpread: true, + }, + }, + plugins: [ + 'markdown', + 'react', + 'babel', + ], + rules: { + 'func-names': 0, + 'arrow-body-style': 0, + 'react/sort-comp': 0, + 'react/prop-types': 0, + 'react/jsx-first-prop-new-line': 0, + 'import/no-unresolved': 0, + 'no-param-reassign': 0, + 'no-return-assign': 0, + 'max-len': 0, + 'consistent-return': 0, + 'no-redeclare': 0, + } +}; + +if (process.env.RUN_ENV === 'DEMO') { + eslintrc.globals = { + React: true, + ReactDOM: true, + mountNode: true, + }; + + Object.assign(eslintrc.rules, { + 'no-console': 0, + 'eol-last': 0, + 'prefer-rest-params': 0, + 'react/no-multi-comp': 0, + 'react/prefer-es6-class': 0, + }); +} + +module.exports = eslintrc; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 20e3bc4c84..0000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "extends": ["eslint-config-airbnb"], - "env": { - "browser": true, - "node": true, - "mocha": true, - "jest": true, - "es6": true - }, - "parserOptions": { - "ecmaVersion": 6, - "ecmaFeatures": { - "jsx": true, - "experimentalObjectRestSpread": true - } - }, - "plugins": [ - "markdown", - "react", - "babel" - ], - "rules": { - "comma-dangle": 0, - "func-names": 0, - "prefer-const": 0, - "arrow-body-style": 0, - "react/sort-comp": 0, - "react/no-multi-comp": 0, - "react/prop-types": 0, - "react/prefer-es6-class": 0, - "react/prefer-stateless-function": 0, - "react/jsx-closing-bracket-location": 0, - "react/jsx-no-bind": 0, - "no-param-reassign": 0, - "no-return-assign": 0, - "max-len": 0, - "consistent-return": 0 - } -} diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index c35dd85b40..c8a17cbf92 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -13,7 +13,7 @@ The following guidelines are about *How to avoid Homework Questions*. ### 1. Read the documentation. -It sad but true that someone just glance(not read) [Ant Design's documentation](http://ant.design/). Please read the documentation closely. What's more, you can modify and run our demo with [CodePen](http://codepen.io/anon/pen/pgdXYp?editors=001). It's helpful to understand our documentation. +It sad but true that someone just glance(not read) [Ant Design's documentation](http://ant.design/). Please read the documentation closely. What's more, you can modify and run our demo with [CodePen](http://codepen.io/anon/pen/wGOWGW?editors=001). It's helpful to understand our documentation. Tips: choose the corresponding documentation with versions selector which in the bottom-right corner. @@ -36,7 +36,7 @@ It is a good habit which will save maintainers' time. Thank you! ## Providing a demo while reporting a bug -It would be helpful to provide a demo which can re-produce the bug 100%. Please fork this [CodePen](http://codepen.io/anon/pen/pgdXYp?editors=001) and re-produce the bug you met. Then, create an issue. The most important thing is: double check before claiming that you have found a bug. +It would be helpful to provide a demo which can re-produce the bug 100%. Please fork this [CodePen](http://codepen.io/anon/pen/wGOWGW?editors=001) and re-produce the bug you met. Then, create an issue. The most important thing is: double check before claiming that you have found a bug. ## Tips about Feature Request diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 198e2c64af..df4acf956e 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,25 +1,27 @@ -If you want to ask a question, please read [some tips](https://github.com/ant-design/ant-design/blob/master/.github/CONTRIBUTING.md#do-your-homework-before-asking-a-question) first :-) + -If you are going to report a bug, please answer the following questions, thank you! + -## What you did +#### 本地环境 -(e.g. I ate a hamburger) + -## What you would like to happen +- antd 版本: +- 操作系统及其版本: +- 浏览器及其版本: -(e.g. I should be full) +#### 你做了什么? -## What actually happened + -(e.g. But I am still hungry now T_T) +#### 你期待的结果是: -## Online demo + -(e.g. http://codepen.io/anon/pen/pgdXYp?editors=001) +#### 实际上的结果: -## Environment Information + -- The version of antd(e.g. 0.12.0): -- Your operating system and it's version(e.g. OSX 10.11.0): -- Your browser and it's version(e.g. Chrome 48.0.0.0(64-bit)): +#### 可重现的在线演示 + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f9760a3504..01e6ca426d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,6 +2,7 @@ First of all, thanks for your contribution! :-) Please makes sure these boxes are checked before submitting your PR, thank you! -[ ] Run `npm run lint` and fix those errors before submitting in order to keep consistent code style. -[ ] Rebase before creating a PR to keep commit history clear. -[ ] Add some descriptions and refer relative issues for you PR. +* [ ] Make sure you follow antd's [code convention](https://github.com/ant-design/ant-design/wiki/Code-convention-for-antd). +* [ ] Run `npm run lint` and fix those errors before submitting in order to keep consistent code style. +* [ ] Rebase before creating a PR to keep commit history clear. +* [ ] Add some descriptions and refer relative issues for you PR. diff --git a/.gitignore b/.gitignore index 16d51bb2c5..86665f9ab8 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,11 @@ node_modules/ npm-debug.log nohup.out _site +_data dist -lib +/lib elasticsearch-* -config/base.yaml \ No newline at end of file +config/base.yaml +typings +components/**/*.js +components/**/*.jsx diff --git a/.lesshintrc b/.lesshintrc index 3aeecaf82e..d798ace465 100644 --- a/.lesshintrc +++ b/.lesshintrc @@ -8,5 +8,12 @@ "qualifyingElement": false, "duplicateProperty": false, "importPath": false, - "finalNewline": false + "finalNewline": false, + "excludedFiles": [ + "components/layout/style/mixin.less", + "components/style/core/base.less", + "components/style/core/iconfont.less", + "components/style/core/normalize.less", + "components/style/mixins/compatibility.less" + ] } diff --git a/.travis.yml b/.travis.yml index ac4e4ca10b..517c87fa04 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,5 +3,4 @@ sudo: false language: node_js node_js: - - "4" - "5" diff --git a/404.html b/404.html deleted file mode 100644 index 3dad29c419..0000000000 --- a/404.html +++ /dev/null @@ -1,6 +0,0 @@ -

找不到此页,三秒后返回首页...

- diff --git a/AUTHORS.txt b/AUTHORS.txt index b9f602e5d8..c796a807a8 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -1,5 +1,8 @@ Benjy Cui +Bernie Bozhao +Bruce Mitchener +Bruno Maia Cam Song Cee Cirno Daqi Song @@ -12,18 +15,31 @@ James KgTong Leon Shi MG12 +Ma Tianxiao +Marshall Chen +Mr.Tone Neverland Pyiner +Qiaosen Huang RaoHai +Sebastian Blade +ShiTengFei SimaQ -The Gitter Badger +Wei Zhu Yuwei Ba Zap afc163 +bang +bang88 chencheng (云谦) +ddcat1115 +devqin +ecofe <150641329@qq.com> elrrrrrrr +fengmk2 genie haoxin +hi-caicai ioldfish jiang <155259966@qq.com> kasinooya @@ -31,24 +47,30 @@ leon.shi lgmcolin lgmcolin pizn +plandem popomore +qubaoming shelwin shouyong simaQ +snadn sorrycc +swindme tom ustccjw <317713370@qq.com> warmhug -xiaoshuai +wizawu yiminghe yubozhao yuche +z +zack zhangpc zhujun24 zilong zinkey 低位 偏右 -唐帅佶 白羊座小葛 逸达 +闲耘™ diff --git a/CHANGELOG.md b/CHANGELOG.md index 080eddfe0e..2e613b1d5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,529 +1,410 @@ -# 更新日志 +--- +order: 3 +title: 更新日志 +toc: false +timeline: true +--- -- category: 4 +如果需要查看 `0.12.x` 及之前的更新日志,请移步 [GitHub](https://github.com/ant-design/ant-design/releases?after=1.0.0)。 --- -## 0.12.8 +## 1.9.1 -`2016-03-06` +`2016-08-16` -- 新增 `heart` `calculator` 图标。 -- Table 补充了 `showHeader` 和 `footer` 属性。 -- Modal 补充了 `maskClosable` 属性。 -- 修正一个 Tag 和 Popover 配合使用的问题。[#1111](https://github.com/ant-design/ant-design/issues/1111) -- 将 TreeSelect 的 `treeDefaultExpandAll` 默认属性设为 false,并优化了性能。 -- 修复 RadioGroup 无法垂直布局的问题。[#1119](https://github.com/ant-design/ant-design/issues/1119) -- 统一了 less 文件的部分变量。 -- 修正部分组件的样式。 +- 修复 Tabs 的 `type="editable-card"` 模式,activeKey 错误的问题。[#2725](https://github.com/ant-design/ant-design/issues/2725) +- 修复一个 Table 的样式兼容性问题。[#2723](https://github.com/ant-design/ant-design/issues/2723) +- 更新 axure 部件库。[#2714](https://github.com/ant-design/ant-design/issues/2714) -## 0.12.7 +## 1.9.0 -`2016-03-03` +`2016-08-15` -- 修正 Table 的 `rowSelect.onSelectAll` 的第三个参数 `deselectedRows` 为 `changeRows`,记录每次变化的列。 - -## 0.12.6 - -`2016-03-02` - -- 优化 Table 本地排序在 Chrome 下的稳定性问题。 -- 修正 Pagination 的 pageSize 属性为受控属性,并补充了 `defaultPageSize` 属性。[#1087](https://github.com/ant-design/ant-design/issues/1087) -- 优化 Select 的 combobox 模式的交互体验。 -- 升级 react-slick 依赖到 `0.11`,修复自动播放有时会失效的问题。[#1009](https://github.com/ant-design/ant-design/issues/1009) -- 修复 TreeSelect 的 allowClear 属性失效的问题。[#1084](https://github.com/ant-design/ant-design/issues/1084) -- 修复 Input 组件的 className 属性失效的问题。[#1103](https://github.com/ant-design/ant-design/issues/1103) -- 修复 Dropdown 的 onClick 属性失效的问题。[#1097](https://github.com/ant-design/ant-design/issues/1097) -- 修复多个 CheckboxGroup 选项互相影响的问题。[#1101](https://github.com/ant-design/ant-design/pull/1101) -- 修复 FormItem 的子元素为 `null` 时报错的问题。 -- 修复 Table 组件的选择功能和展开功能配合使用的问题。[#1102](https://github.com/ant-design/ant-design/issues/1102) -- 增加了一个搜索框和提示功能结合的 [例子](http://ant.design/components/select/#demo-search-box)。 -- 允许可编辑的 Tabs 删除最后一个页签。[#1071](https://github.com/ant-design/ant-design/issues/1071) -- Table 的 `rowSelect.onSelectAll` 补充了第三个参数 `deselectedRows`, `rowSelect.onChange` 补充了第二个参数 `selectedRows`。[#1105](https://github.com/ant-design/ant-design/issues/1105) -- 修正部分组件的样式。 - -## 0.12.5 - -`2016-02-25` - -- Pagination 支持 `showTotal` 属性。[#1077](https://github.com/ant-design/ant-design/pull/1077) -- Cascader 添加 `allowClear` 属性,允许隐藏清除按钮。 -- 补充了一个带图标的搜索建议框的例子。[#1067](https://github.com/ant-design/ant-design/issues/1067) -- 修复 Transfer 在不支持 Object.assign 的浏览器上报错的问题。 -- 修复 Cascader 在 Safari 下错位的问题。[#1066](https://github.com/ant-design/ant-design/issues/1066) -- 移除对 Button 圆角的降级方案。 -- 修复了部分组件样式的小问题。 - -## 0.12.4 - -`2016-02-22` - -- Radio 的 value 支持更多类型。[#1043](https://github.com/ant-design/ant-design/pull/1043) -- 修复 Spin 组件的大小、居中等样式问题。 -- FormItem 补充 extra 属性的文档。[#931](https://github.com/ant-design/ant-design/issues/931) -- 修复的 Table 下树形数据和选择框配合时的样式问题。 -- 修复一个水平表单的错误提示的样式错位问题。[#1040](https://github.com/ant-design/ant-design/issues/1040) -- 添加了一个轻微的 Button 点击动效。 - -## 0.12.3 - -`2016-02-19` - -- DatePicker 补充 allowClear 属性,支持单选的清空。 -- 修复显示时间的 DatePicker 的清空按钮失效的问题。 -- 优化 DatePicker 的 `确定` 按钮失效样式。 - -## 0.12.2 - -`2016-02-19` - -- DatePicker 如果有 `确定` 按钮,现在只有点击 `确定` 按钮才会触发 onChange 事件。 -- 修复带时间选择的 DatePicker 日期格式缺少时间部分的问题。[#1005](https://github.com/ant-design/ant-design/issues/1005) -- 修复 DatePicker 内输入框多余的时间展示的问题。[#953](https://github.com/ant-design/ant-design/issues/953) -- 升级依赖 react-slick 到 `0.10`。 -- 支持表单校验错误时自动滚动到第一个错误项。[#993](https://github.com/ant-design/ant-design/issues/993) -- 优化了 Select 和 TreeSelect 多选禁用的样式。 -- Upload 列表项支持链接展现形式。[#1013](https://github.com/ant-design/ant-design/issues/1013) -- 修复 Safari 下的样式警告信息。[#999](https://github.com/ant-design/ant-design/issues/999) -- Cascader 支持 popupPlacement 位置配置。 - -## 0.12.1 - -`2016-02-03` - -- 依赖升级到 `rc-pagination@1.4`、`rc-menu@4.10`、`rc-form@0.12`。 -- 修复 TreeSelect 的不可用样式。 -- DatePicker 补充 `getCalendarContainer` 属性,用于解决问题 [#991](https://github.com/ant-design/ant-design/issues/991)。 -- 修正 Modal `onCancel` 的参数为点击事件。[#980](https://github.com/ant-design/ant-design/issues/980) -- 修复一个 Tooltip 内嵌套 Popconfirm 的问题。[#977](https://github.com/ant-design/ant-design/issues/977) -- 修复 DatePicker 和 RangePicker 的 `onOk` 一直不可用的问题。 -- 修复一个 Badge 的 count 无法切换的问题。[#983](https://github.com/ant-design/ant-design/issues/983) - -## 0.12.0 - -`2016-02-01` - -- 新增 [级联选择(Cascader)](http://ant.design/components/cascader/) 组件。 -- 新增 [树选择控件(TreeSelect)](http://ant.design/components/tree-select/) 组件。 -- Form 自身支持校验功能,废弃 Validation。[演示](http://ant.design/components/form/#demo-validate-basic) -- Tabs - - `activeKey` 修正为受控属性。 - - 当前项现在会始终显示。[#815](https://github.com/ant-design/ant-design/issues/815) -- Modal 可以配置右上关闭按钮是否显示。 -- Select - - 打开选项菜单时,自动滚动到选中项。 - - `combobox` 模式时,可配置是否默认选中第一项。[rc-select#38](https://github.com/react-component/select/issues/38) +- Transfer 修复在火狐下 item 文案过长时只显示省略号的问题。[#2674](https://github.com/ant-design/ant-design/issues/2674) +- Input 修复 `autosize` 模式下特定场景中不能输入中文及光标定位不准的问题。[#2666](https://github.com/ant-design/ant-design/issues/2666) [#2239](https://github.com/ant-design/ant-design/issues/2239) +- Tabs 修复 `type="editable-card"` 模式下的 `children` 解析问题。[#2658](https://github.com/ant-design/ant-design/issues/2658) +- Radio 修复若干 less 硬编码问题。[#2424](https://github.com/ant-design/ant-design/issues/2424) +- Upload 的 rc-upload 依赖升级至 2.x,引入的变化有: + - 增加 `disabled` 属性。[#2645](https://github.com/ant-design/ant-design/issues/2645) + - 取消上传时会自动 abort 上传请求。[#2571](https://github.com/ant-design/ant-design/issues/2571) [#2518](https://github.com/ant-design/ant-design/issues/2518) - Table - - filter 支持层级选择。 - - 支持行点击事件 `onRowClick`。 - - 支持多列的横向切换。[演示](http://ant.design/components/table/#demo-paging-columns) - - 更换 `dataSource` 和变换页面时不再默认清除选择数据,你可以用 `selectedRowKeys` 手动控制。`原来默认清除的行为会触发一个数据更新的死循环,而且难以实现跨页选择。` - - 支持固定表头。[演示](http://ant.design/components/table/#demo-fixed-header) -- Tag 去除 `href` 属性,默认标签名从 `a` 改为 `span`。 -- Timeline 支持指定 pending 节点的内容。 -- Tree - - 节点支持拖拽。 - - 支持动态控制节点展开与否。[演示](http://ant.design/components/tree/#demo-basic-controlled) - - 可以监听节点展开/关闭事件 `onExpand`。 - - `onCheck` `onSelect` 参数调整。 - - `onDataLoaded` 改为 `loadData`。 - - 新增 drag&drop 相关属性: - - `onDragStart` - - `onDragEnter` - - `onDragOver` - - `onDragLeave` - - `onDrop` - - 新增 TreeNode 节点属性: - - `disableCheckbox` - - `isLeaf` -- Transfer 给 `onChange` 增加参数。[#972](https://github.com/ant-design/ant-design/issues/972) + - 修复 spin 在可滚动区域的定位问题。[#2652](https://github.com/ant-design/ant-design/issues/2652) + - 修复无数据时 提示样式错位的问题。[#2663](https://github.com/ant-design/ant-design/issues/2663) +- Popover 修复设定 `getTooltipContainer` 后会导致内嵌 DatePicker 样式失效的问题。[#2675](https://github.com/ant-design/ant-design/issues/2675) +- Modal 修复重复卸载组件导致的报错。[#2688](https://github.com/ant-design/ant-design/issues/2688) +- 升级 rc-slider 组件依赖。 + +## 1.8.0 + +`2016-08-08` + +- 修复可关闭 Tabs 组件只有一个 Tab 的时候报错的问题。[#2559](https://github.com/ant-design/ant-design/issues/2559) +- 修复 Datepicker 在 IE8 下关闭图标。[#2584](https://github.com/ant-design/ant-design/issues/2584) +- Tags 支持自定义标签颜色。[#2585](https://github.com/ant-design/ant-design/issues/2585) +- TreeSelect 修复未找到内容时的样式。[9cee9f](https://github.com/ant-design/ant-design/commit/9cee9f103a4729572358206c81cba84e2fdc20f5) +- Modal 适配小屏幕。[#2597](https://github.com/ant-design/ant-design/issues/2597) +- 修复了 Row 组件在同一行闭合会报错的问题。[#2603](https://github.com/ant-design/ant-design/issues/2603) +- Table 的 `rowSelection.onChange` 的参数 `selectedRows` 现在和 `selectedRowKeys` 保持一致。[#2566](https://github.com/ant-design/ant-design/issues/2603) +- Checkbox 和 Radio 现在支持 `onClick` 属性。 + +## 1.7.0 + +`2016-07-30` + +友情提示 [Ant Design Mobile](http://mobile.ant.design) 已经发布。 + +- Table + - 现可以定义页头。[demo](http://ant.design/components/table#components-table-demo-bordered) + - 修复当 `rowKey` 为 `String` 时的报错问题。[#2500](https://github.com/ant-design/ant-design/issues/2500) + - 修复 `Table` 会修改 `dataSource` 里面的值的问题。[#2501](https://github.com/ant-design/ant-design/issues/2501) +- Form 现在不再需要显式传递 `form={this.props.form}`。 +- 优化 Breadcrumb.Item 的 hover 效果。 +- 优化 Progress 的动画效果。 - DatePicker - - 修复 RangePicker 开始结束日期相同的 bug。[#822](https://github.com/ant-design/ant-design/issues/822) - - 修复 `format` 对浮层不生效问题。[#917](https://github.com/ant-design/ant-design/issues/917) -- TimePicker 修复一个 `value` 为 `null` 时没有进入受控模式的问题。 -- Upload - - 可以用 `headers` 设置上传头部。 - - 新增上传图片卡片样式。[演示](http://ant.design/components/upload/#demo-picture-card) -- Radio - - 更换 Radio.Button 的展现样式。 - - 可以设置 Radio.Button 的大小。 -- Progress - - `format` 属性现在支持自定义 function 的方式进行定义。[#893](https://github.com/ant-design/ant-design/issues/893) - - `format` 指定 string 和 React.Node 的方式被废弃。 - - 支持 `style` 属性。[#895](https://github.com/ant-design/ant-design/issues/895) -- message && notification 现在可以销毁。 -- Button - - 小号 Button 的圆角调整为 `4px`。 - - 修复 Button.Group disabled 后的样式问题。[#926](https://github.com/ant-design/ant-design/issues/926) -- BreadCrumb - - 移除 `router` 属性,无需设置。 - - 修复一个链接参数不对的问题。 + - 优化清除按钮样式。 + - 修复点击 `此刻` 时不触发 `onChange` 的问题。[#1902](https://github.com/ant-design/ant-design/issues/1902) +- Menu + - 修复子菜单中的 Item 被选中后,父级元素无样式变化的问题。[#2414](https://github.com/ant-design/ant-design/issues/2414) + - 修复 Menu.Item disabled 后的样式问题。 +- TreeSelect + - treeNodes 可以设置是否可选。[#2401](https://github.com/ant-design/ant-design/issues/2401) + - 修复多选模式下进行搜索会把已选项清掉的问题。[#2393](https://github.com/ant-design/ant-design/issues/2393) + - 修复 TreeSelect 会修改原数据的问题。[#2459](https://github.com/ant-design/ant-design/issues/2459) +- 修复了 Select 组件 placeholder 溢出的问题。[#2480](https://github.com/ant-design/ant-design/pull/2480) +- 修复 Timeline.Item 无法自定义边框颜色的问题。[#2479](https://github.com/ant-design/ant-design/issues/2479) +- 修复 Spin 显示突兀的问题。[#2398](https://github.com/ant-design/ant-design/issues/2398) +- 修复 Cascader 选项文字过长导致的样式问题。[#2515](https://github.com/ant-design/ant-design/issues/2515) -## 0.11.3 +## 1.6.5 -`2016-01-19` +`2016-07-16` -- 修复 TimePicker 受控模式点选即关闭面板的问题。[#818](https://github.com/ant-design/ant-design/issues/818) -- 修复一个两栏的 TimePicker 点击右边空白处无法关闭面板的问题。[#826](https://github.com/ant-design/ant-design/issues/826) -- 修复 Table `pagination.onChange` 指定无效的问题。[#824](https://github.com/ant-design/ant-design/issues/824) -- 修复 Transfer 搜索功能失效的问题。 -- 修复 DatePicker 的 MonthPicker 样式错乱的问题。 -- 修复 RangePicker 时区无法设置的问题。[#837](https://github.com/ant-design/ant-design/issues/837) -- 修复二维码图标,新增一个扫描图标。[#772](https://github.com/ant-design/ant-design/issues/772) +- 修复 Input 的 `value prop on input should not be null` 警告并且导致在表单中无法重置的问题。[#2335](https://github.com/ant-design/ant-design/issues/2335) +- 优化 FormItem 的布局实现,修复表单布局不支持响应式布局的问题。[#2305](https://github.com/ant-design/ant-design/issues/2305) +- 修复带时间的 DatePicker 的 onChange 触发逻辑。[#2399](https://github.com/ant-design/ant-design/issues/2399#issuecomment-232893146) +- 修复 Transfer 搜索后全选的问题。[#2396](https://github.com/ant-design/ant-design/issues/2396) +- 修复 Cascader 样式会被 ant-input 样式覆盖的问题。[#2400](https://github.com/ant-design/ant-design/issues/2400) +- 修复 Table 删除数据时导致当前页数溢出的问题。[#2301](https://github.com/ant-design/ant-design/pull/2301) +- 修复 resize 浏览器时 Affix 元素没有和原来的位置同步的问题。[#1987](https://github.com/ant-design/ant-design/issues/1987) +- 给 Affix 元素添加占位,修复固定时页面跳动的问题。 +- 修复 Select combobox 模式会导致页面出现横向滚动条的问题。[#2353](https://github.com/ant-design/ant-design/issues/2353) +- 修复 Upload 组件已上传文件链接点击无效的问题。[#2331](https://github.com/ant-design/ant-design/issues/2331) +- 修复 Upload 上传过程中删除图片后的报错问题。[#2342](https://github.com/ant-design/ant-design/issues/2342) -## 0.11.2 +## 1.6.4 -`2015-01-03` +`2016-07-08` -- 新增了[贡献文档](https://github.com/ant-design/ant-design/blob/master/CONTRIBUTING.md)。 -- 修复一个 DatePicker 中选择的国际化文案问题。[#771](https://github.com/ant-design/ant-design/issues/771) -- 增加了一个高级搜索类型表单的[演示](http://ant.design/components/form/#demo-advanced-search-form)。 -- Dropdown 支持多级的下拉菜单。[演示](http://ant.design/components/dropdown/#demo-sub-menu) +- 修复组件在 react@15.2.0 下报 Unknown props 警告的问题。[#2258](https://github.com/ant-design/ant-design/issues/2258) +- `Table` + - 修复 filterDropDown 中内容未改变也会调用 onChange 的问题。[#2228](https://github.com/ant-design/ant-design/issues/2228) + - 修复设置 scroll.y 高度后导致内容无法对齐的问题。[#2227](https://github.com/ant-design/ant-design/issues/2227) +- `Form` + - 修复 `FormItem` 中带空格后缀的冒号替换问题,关联issue:[#1877](https://github.com/ant-design/ant-design/issues/1877) + - demo 优化。 +- `Transfer` + - 修复重复 render 的问题,性能优化。[#2112](https://github.com/ant-design/ant-design/issues/2112) + - 优化搜索逻辑,修复搜索时未对特殊字符进行处理的问题。[#2260](https://github.com/ant-design/ant-design/issues/2260) + - 清除按钮样式优化。 +- 修复 `Steps` 最后一步多余横线隐藏的问题。 +- 修复 `Cascader` small size 样式下沉 1px,以及 hover/click 样式残缺的问题。[#2234](https://github.com/ant-design/ant-design/issues/2234) +- 修复 `RangePicker` 无清除按钮的问题。[#2252](https://github.com/ant-design/ant-design/issues/2252) + +## 1.6.3 + +`2016-07-04` + +- 修复 Transfer 的一个 unmount 的错误。[#2206](https://github.com/ant-design/ant-design/pull/2206) +- 修复了 Badge、Alert、Menu、Tag、Checkbox、Radio 组件的一些样式细节问题。 + +## 1.6.2 + +`2016-06-27` + +- 修复 Table、Transfer 的样式错位问题。 +- 修复 DatePicker 的一个样式问题。[#2182](https://github.com/ant-design/ant-design/issues/2182) +- 优化 Menu 的 hover 样式响应性能。 + +## 1.6.1 + +`2016-06-24` + +- 回滚一个未完成的 DatePicker 时间选项改造效果。 + +## 1.6.0 + +`2016-06-24` + +- 新增置顶组件 [BackTop](/components/back-top)。 +- 全新的 [Spin](/components/spin) 样式。 +- 给 `Modal.xxx` 系列方法添加了 `{ destory }` 的访问值,方便事后销毁。[#2110](https://github.com/ant-design/ant-design/issues/2110) +- Table 的 `rowKey` 属性支持直接使用字符串。[#2058](https://github.com/ant-design/ant-design/issues/2058) +- Table 增加 `column.filterDropdown` 属性用于自定义渲染筛选菜单的浮层。[#1736](https://github.com/ant-design/ant-design/issues/1736) +- 修复 Tooltip、Popover、Popconfirm 设置 `onVisibleChange` 后失效的问题。[#2134](https://github.com/ant-design/ant-design/issues/2134) +- 修复在 IE8 下 Checkbox 的勾样式变形的问题。[#2148](https://github.com/ant-design/ant-design/issues/2148) +- 优化 Checkbox、Radio 失效状态的文字颜色。[#2114](https://github.com/ant-design/ant-design/issues/2114) +- 优化 Checkbox、Radio 的默认边距过于拥挤的问题。[#2137](https://github.com/ant-design/ant-design/issues/2137) +- 优化 Pagination 在暗色背景下的样式。[#2126](https://github.com/ant-design/ant-design/issues/2126) +- 修复 Table 固定列时内容无法换行和高度对齐的问题,同时修复了一个 Chrome 下的表格内容错位问题。[#2130](https://github.com/ant-design/ant-design/issues/2130) +- 修复一个 Table 的 `rowSelection` 设为 null 时可能导致报错的问题。[#2127](https://github.com/ant-design/ant-design/issues/2127) +- 修复在 IE8 下点击 Table 选择框报错的问题。[#2154](https://github.com/ant-design/ant-design/issues/2154) +- 小幅优化了 Transfer 的渲染性能。[#2112](https://github.com/ant-design/ant-design/issues/2112) +- 将 DatePicker 的清除按钮从面板上移到外部输入框,解决用户容易误解为关闭的问题。[#1708](https://github.com/ant-design/ant-design/issues/1708) +- Upload 的 `onPreview` 现在没有 `file.url` 时也能生效。[#2163](https://github.com/ant-design/ant-design/issues/2163) + +## 1.5.1 + +`2016-06-21` + +- 修复一个 TypeScript 定义文件的语法错误。 +- 修复 Table 固定表头高度和滚动条样式问题。 + +## 1.5.0 + +`2016-06-17` + +- 升级 `rc-form` 到 0.17,支持 `getFieldProps('xx.yy')` 的写法,并支持单多选控件进行关联。[#](https://github.com/react-component/form/pull/21) +- Input 的 `addonBefore` 和 `addonAfter` 支持内嵌选择框。[#1927](https://github.com/ant-design/ant-design/issues/1927) +- 优化了两个 DatePicker 组成的时间范围选择演示的体验。 +- 优化一个多个对话框的遮罩层高度的问题。[#2009](https://github.com/ant-design/ant-design/issues/2009) +- 优化 Table 的 `getCheckboxProps` 的调用次数。[#2086](https://github.com/ant-design/ant-design/issues/2086) +- 修复 Table 固定列时,表头无法左右滚动的问题。[#2068](https://github.com/ant-design/ant-design/issues/2068) +- 修复小型表格固定表头的样式。[#2023](https://github.com/ant-design/ant-design/issues/2023) +- 修复 Tabs 的 `tabPosition` 为左右时样式错位的问题。[#2046](https://github.com/ant-design/ant-design/issues/2046) +- 修复 RangePicker 的日期范围背景丢失的问题。 +- 修复 Switch 失效状态下文字颜色太浅的问题。[#2051](https://github.com/ant-design/ant-design/issues/2051) +- 修复一个 Select 的 `disabled` 选项依然可以被移除的问题。[#2034](https://github.com/ant-design/ant-design/issues/2034) +- 修复官方站点在 IE 下的报错问题。 + +## 1.4.1 + +`2016-06-12` + +- 修复一个展开 Tabs 会导致表格宽度溢出的问题。[#2013](https://github.com/ant-design/ant-design/issues/2013) +- 修复一个某些情况下表格布局被破坏的问题。 + +## 1.4.0 + +`2016-06-12` + +此版本之后你可能会遇到 [#2030](https://github.com/ant-design/ant-design/issues/2030),请使用 `react@15+` 或 `npm@3+`。 + +- `Input[type="textarea"]` 支持自动调整高度。 [#](http://ant.design/components/input#components-input-demo-autosize-textarea) +- `Breadcrumb` + - `nameRender` 新增 `route` 和 `params` 参数。 [#1999](https://github.com/ant-design/ant-design/issues/1999) + - `linkRender` 新增 `paths` 参数。 +- 再次修复 `Table` 组件 `rowSelection.onChange` 与 `onRowClick` 冲突问题。 [#1470](https://github.com/ant-design/ant-design/issues/1470) +- 修复 `Form.Item` 中 `Input` 高度抖动问题。 [#1955](https://github.com/ant-design/ant-design/issues/1955) +- 修复高级搜索的 `ant-advanced-search-form` 样式丢失的问题。 + +## 1.3.2 + +`2016-06-06` + +- 修复全局模式下引用 antd,IE8 环境报错的问题。 [#1970](https://github.com/ant-design/ant-design/issues/1970) + +## 1.3.1 + +`2016-06-06` + +- 修复 `Message` `Notification` 找不到的问题。 [#1968](https://github.com/ant-design/ant-design/issues/1968) + +## 1.3.0 + +`2016-06-02` + +- Transfer 组件增加 `rowKey` 属性,可自定义数据源主键。 [#1900](https://github.com/ant-design/ant-design/issues/1900) +- Tag 组件 `default` 类型的样式增加边框,防止淹没在背景中。 [#1910](https://github.com/ant-design/ant-design/issues/1910) - Table - - 新增 `rowSelection.onChange` 和 `rowSelection.selectedRowKeys`,完善选择功能。 - - 更新 dataSource 时,选中项现在会被清空。 - - 修复一个全选框和禁用的选择项配合的问题。 -- 修复 `0.11.1`版本 menu 内嵌型菜单(inline)选中后关闭的问题。 -- 修复 `0.11.1`版本对 React 版本要求太严的问题,对应的警告提示对于 `0.14.x` 将不再出现。 -- 组件和文档的样式小调整。 + - 修复筛选为单选时仍旧展示多选框的问题。 [#1880](https://github.com/ant-design/ant-design/issues/1880) + - 修复 fixed left 的固定列会覆盖 rowSelection 的 Checkbox 的问题。 [#1829](https://github.com/ant-design/ant-design/issues/1829) + - 升级 rc-table 依赖 + - 修复了 fixed 列中数据重复展示以及一些错位问题。 [#1898](https://github.com/ant-design/ant-design/issues/1898) + - `dataIndex` 支持内嵌属性的写法。 [react-component/table#46](https://github.com/react-component/table/issues/46) +- 修复了 v1.2.0 新增加的组件属性的 TypeScript 定义。 [#1933](https://github.com/ant-design/ant-design/issues/1933) +- Form 修复 label中冒号的国际化问题,采用样式实现冒号,不再需要手动输入冒号。 [#1877](https://github.com/ant-design/ant-design/issues/1877) +- 修复 DatePicker 组件点击『此刻』失效的问题,并进行了一些代码优化。 [#1902](https://github.com/ant-design/ant-design/issues/1902) +- 升级 rc-upload 依赖,修复了 IE10 中第二次上传同一文件不触发 `onChange` 的问题。 [058af3c](https://github.com/ant-design/ant-design/commit/b15a4e3165be5e4db995d3fe75d4d557c7f21c61) +- 文档使用 [bisheng](https://github.com/benjycui/bisheng) 重构。 -## 0.11.1 +## 1.2.1 -`2015-12-29` +`2016-05-27` -- 修复一个 Table 无法修改 pageSize 的问题。 -- 修复一个 Table 子表格展开的对齐问题。 -- 修复一个 Chrome 下部分图标左侧切边的问题。 -- 修复搜索输入框在表单下使用的样式问题。[#762](https://github.com/ant-design/ant-design/issues/762) +- 修复一个 Select 组件的文字重复问题。 -## 0.11.0 +## 1.2.0 -`2015-12-28` +`2016-05-26` -- **移除默认加载的样式文件,样式现在需要独立加载。** -- 按钮圆角调整为 `6px`。 -- Modal、Popconfirm、Table、TimePicker 支持国际化配置。 -- 新增虚线型按钮。 -- 新增 [通用搜索框](http://ant.design/components/form/#demo-search-input) 样式。 -- 新增图片上传列表样式[演示](http://ant.design/components/upload/#demo-picture-style)。 -- **部分设计资源开放 [下载](http://ant.design/spec/tools),包括 Axure 组件库和 Iconfont 字体打包文件。** -- 新增 [吊顶规范](http://ant.design/spec/layout/#demo-ceiling)。 -- 组件演示页面增加锚点。 -- 新增穿梭框 [Transfer](http://ant.design/components/transfer/) 组件。 -- 新增小尺寸的 Switch 开关组件。 -- 增加更多的图标。[#](https://github.com/ant-design/ant-design/commit/087c64649d73206a4d62e52f9b3f6042c1d28608#diff-dc1a1f4794c1c4ee3b083381d4c50c47R180) -- 全局微调了警告和错误状态色。 +- Input 组件的文档现在和 Form 分离。 [3c98d3](https://github.com/ant-design/ant-design/commit/3c98d3f80f4ec80066756adc3b4108141d4383ca) +- Affix + - 新增了 `onChange` 属性。当固定状态改变时回调 [#1777](https://github.com/ant-design/ant-design/issues/1777) + - 找回了从 affixStyle 中走失的 `width` 属性,修复固定后错位的问题。[#1820](https://github.com/ant-design/ant-design/issues/1820) +- Table + - 修复了 Table 组件的分页相关的一系列问题 [#1669](https://github.com/ant-design/ant-design/issues/1669) [#1842](https://github.com/ant-design/ant-design/issues/1842) + - 修复了当有列固定在左边时,选择框不显示的问题 [#1829](https://github.com/ant-design/ant-design/issues/1829) +- 修复了当 Checkbox 的 label 为数字 0 时, label 不显示的问题 [#1811](https://github.com/ant-design/ant-design/issues/1811) +- 修复 Select combobox 模式下无法重置 `optionLabelProp` 的问题。[#1773](https://github.com/ant-design/ant-design/issues/1773) +- 修复了 Tag 组件为 closeable 时,内部链接无法点击的问题 [#1862](https://github.com/ant-design/ant-design/issues/1862) +- Tab 组件新增 `hideAdd` 属性,用于关闭右边的添加按钮 [#1750](https://github.com/ant-design/ant-design/issues/1750) +- 修复了一个在某些情况下找不到 `normalize.css/normalize.css` 文件的问题。[ant-design/antd-init#52](https://github.com/ant-design/antd-init/issues/52) +- 修复构建文件在 IE8 下报错的问题。[#1804](https://github.com/ant-design/ant-design/issues/1804) +- 更新了第三方依赖。 + +## 1.1.0 + +`2016-05-18` + +- Cascader 的选择框支持自定义渲染节点,并给 `displayRender` 方法增加了 `selectedOptions` 参数。[#1726](https://github.com/ant-design/ant-design/issues/1726) +- Input.Group 新增 `size` 属性,可设置控件尺寸。[#1732](https://github.com/ant-design/ant-design/issues/1732) +- Layout 新增常用布局:侧边导航展开收起模式。[#1643](https://github.com/ant-design/ant-design/issues/1643) +- Transfer 支持自定义渲染行数据。[#1664](https://github.com/ant-design/ant-design/issues/1664) +- Upload 的 children 为空时,不再显示上传按钮。[#1610](https://github.com/ant-design/ant-design/issues/1610) +- Table + - 修复 `filter` 过滤数据后显示错误分页的问题。[#1669](https://github.com/ant-design/ant-design/issues/1669) + - 修复 `pagination` 不指定时显示错误分页的问题。[#1683](https://github.com/ant-design/ant-design/issues/1683) +- Modal + - 修复弹出时背景依然跟随滚动的问题。[#1751](https://github.com/ant-design/ant-design/issues/1751) + - 修复关闭按钮获得焦点时的样式问题。[#1668](https://github.com/ant-design/ant-design/issues/1668) +- 将搜索输入框相关样式移到 Input 组件下。[7b7f846](https://github.com/ant-design/ant-design/commit/7b7f8461611e53f4f96ae8d64d37fe28ee8d2553) +- 修复 Select 获得焦点时的样式问题。[#1684](https://github.com/ant-design/ant-design/issues/1684) +- 修复 TreeSelect 占位符样式问题。[#1657](https://github.com/ant-design/ant-design/issues/1657) +- 修复了类型定义以更好地支持 `TypeScript`。[#1696](https://github.com/ant-design/ant-design/pull/1696) [@xujihui1985](https://github.com/xujihui1985) +- 优化了 LocaleProvider。[a3850a4](https://github.com/ant-design/ant-design/commit/a3850a4df84d7055a1a40600919f2f9ba1bbf2b2) +- 其他组件的样式优化。 + +## 1.0.1 + +`2016-05-11` + +- 修复当 Table 的 `rowSelection.type` 为 'radio' 时的报错。[#1627](https://github.com/ant-design/ant-design/issues/1627) +- 修复 CheckboxGroup 与 `getFieldProps`共用时的问题。[#1631](https://github.com/ant-design/ant-design/issues/1631) +- 修复 RangePicker 中 TimePicker 不会受 locale 控制的问题。[#1635](https://github.com/ant-design/ant-design/issues/1635) +- 修复 Tag 组件缺失的问题。 +- 修复 Table 的 className 不在最外层容器上的问题。 +- 修复一个样式文件重复打包的问题。 + +## 1.0.0 + +`2016-05-09` + +很高兴的通知各位,经过四个月时间的紧密开发,`antd@1.0.0` 终于发布了。从去年 5 月 7 日提交第一行代码以来,经过整整一年的开发迭代,antd 受到社区的大量关注,使用的公司和产品持续增加,已经日趋成熟。这个版本我们重构了底层代码和站点,持续完善现有组件功能和优化细节,其中很多都来自社区的贡献,无法一一感谢,欢迎各位持续关注和鞭策。在升级过程中遇到任何问题,请及时反馈给我们。 + +### 主要变化 + +- **兼容 React@15.x**。 +- **全新单页站点**,使用 React 和 antd 进行了彻底重构,加载更快,访问更流畅。 +- **样式支持按需加载**。可参考 [antd-init](https://github.com/ant-design/antd-init) 的模版代码, 需要配合 [babel-plugin-antd](https://github.com/ant-design/babel-plugin-antd#usage) 插件和 `style` 配置进行使用。[#900](https://github.com/ant-design/ant-design/issues/900) +- **提供独立的构建文件**。[文档](/docs/react/install#浏览器引入) +- 新增卡片组件 [Card](/components/card)。 +- 新增评分组件 [Rate](/components/rate)。 +- 新增 [LocaleProvider](/components/locale-provider) 组件,提供组件文案的国际化支持,并新增了英语和俄语的语言配置。[#1411](https://github.com/ant-design/ant-design/issues/1411) +- 更好的服务端渲染支持,修复了 Badge、Spin、Calendar、Upload 等组件服务端渲染的问题。 +- 新增 antd.d.ts 以更好的支持 TypeScript。[@bang88](https://github.com/bang88) +- 布局组件支持响应式布局和栅格间隔设置。[#1082](https://github.com/ant-design/ant-design/issues/1082) +- Table 支持固定列和横向滚动。[#1265](https://github.com/ant-design/ant-design/issues/1265) + +### 不兼容改动 + +此版本有部分不兼容的改动,升级时确保修改相应的使用代码。 + +- 推荐使用样式按需加载。如果依然需要整体载入样式,**样式入口文件已变为** `antd/dist/antd.css` 和 `antd/dist/antd.less`。如果你在项目中覆盖了 less 变量,less 文件的引用方式也有 [相应变更](https://github.com/ant-design/ant-design/issues/1558#issuecomment-218120000)。 + + ```diff + - import 'antd/lib/index.css'; // import 'antd/style/index.less'; + + import 'antd/dist/antd.css'; // import 'antd/dist/antd.less'; + ``` + +- 完全移除了 `0.12` 中废弃的 Validation 组件,可以直接 import [rc-form-validation](https://github.com/react-component/form-validation) 用以代替。[#1096](https://github.com/ant-design/ant-design/issues/1096) +- Breadcrumb.Item 的 `href` 属性被移除,请直接用 `a` 标签包裹可点击的内容。 +- Modal 移除了 `align` 属性,现在可以使用 `style` 属性调整位置。 +- `Modal.confirm` 等方法的配置项 `iconClassName` 重命名为 `iconType`。 +- Select 移除了 `onChange` 中的 `label` 参数,新增了 `labelInValue` 属性。[#1695](https://github.com/ant-design/ant-design/issues/1695) +- 移除了 `import { Form } from 'antd/lib/form';` 的用法,应统一为 `import { Form } from 'antd';` 或 `import Form from 'antd/lib/form';`。 + +#### 有兼容提示的改动 + +这里的改动在升级后控制台会出现警告提示,请按提示进行修改。 + +- 废弃 QueueAnim,可以直接 import [rc-queue-anim](https://github.com/react-component/queue-anim) 用以代替。Ant Design 的动效方案已移至 [Ant Motion](http://motion.ant.design/components/queue-anim),欢迎前往探索。 +- Affix 的 `offset` 属性重命名为 `offsetTop`。 +- Popover 的 `overlay` 属性重命名为 `content`。 +- Progress.Line 使用方式改为 `` 或 ``。 +- Progress.Circle 使用方式改为 ``。 +- Spin 的 `spining` 属性更正为 `spinning`。 +- Alert 的 type `warn` 重命名为 `warning`。[#1225](https://github.com/ant-design/ant-design/issues/1225) +- Tree 的 `onExpand` 参数从 `function(node, expanded, expandedKeys)` 调整为 `function(expandedKeys, {expanded, node})`。 + +### Bug 修复 + +- 修复 Table 的 `size` 为 `middle` 时,分页器大小无法控制的问题。[#1396](https://github.com/ant-design/ant-design/issues/1396) +- 修复 Table 的 `pagination.defaultCurrent` 失效的问题。 +- 修复 Cascader 的 `defaultValue` 没有被 `value` 覆盖的问题。 +- 修复 Select 同时设置 `allowClear` `disabled` 时还是会出现清除按钮的问题。[#1480](https://github.com/ant-design/ant-design/issues/1480) +- 修复 Transfer 的 `DataSource` 变化时已选中项没有同步的问题。[#1587](https://github.com/ant-design/ant-design/issues/1587) +- 修复 DatePicker 日期格式与国际化配置不同步的问题。[#1509](https://github.com/ant-design/ant-design/issues/1509) +- 修复 Button 禁用时事件仍然会冒泡的问题。[#1541](https://github.com/ant-design/ant-design/issues/1541) +- 修复 Carousel 自动播放时的卡顿和报错问题。[#1397](https://github.com/ant-design/ant-design/issues/1397) +- 修复 Tabs 的 card 类型内嵌标准 Tabs 时的样式问题。[#1617](https://github.com/ant-design/ant-design/issues/1617) +- 修复 Menu `horizontal` 和 `vertical` 模式不支持受控 `openKeys` 的问题。 + +### 其他改进 + +- 样式变量梳理,去除了部分无用的变量,另外还有大量样式细节问题修复。 +- 依赖的 normalize.css 升级到 [4.x](https://github.com/necolas/normalize.css/blob/4.1.1/CHANGELOG.md)。 +- 使用 ES2016 classes 重构了代码。[@waywardmonkeys](https://github.com/waywardmonkeys) +- Popover、Popconfirm 和 Tooltip 组件根据不同的弹出位置有了更精准方向的弹出动画。 +- 补充 Select TreeSelect Switch Radio Checkbox 等组件的 `focus` 表现,增强表单组件的可用性。[#1358](https://github.com/ant-design/ant-design/issues/1358) +- message 和 notification 现在可以全局配置 `duration`。[#1143](https://github.com/ant-design/ant-design/issues/1143) +- DatePicker 和 TimePicker 的 `onChange(date, dateString)` 方法增加第二个参数用于获得格式化后的日期字符串。[#1104](https://github.com/ant-design/ant-design/issues/1104) +- DatePicker 和 DatePicker.RangePicker 现在可以设置内部 TimePikcer 的属性。[#1415](https://github.com/ant-design/ant-design/issues/1415) +- Checkbox + - 支持类似 Radio 的使用方式 `option`。[#1029](https://github.com/ant-design/ant-design/issues/1029) + - Checkbox.Group 现在允许 `label` 和 `value` 不同。[#1025](https://github.com/ant-design/ant-design/issues/1025) + - Checkbox.Group 允许单独设置某个 Checkbox 为 `disabled`。[#1218](https://github.com/ant-design/ant-design/issues/1218) +- Breadcrumb + - 支持路由模式下自定义链接 `linkRender`。[#1026](https://github.com/ant-design/ant-design/issues/1026) + - 支持路由模式下自定义最后一项内容 `nameRender`。[#1304](https://github.com/ant-design/ant-design/issues/1304) +- Modal + - 新增 `Modal.warning` 方法。 + - 弹出时背景不再跟随滚动。[#1195](https://github.com/ant-design/ant-design/issues/1195) - Select - - 选中样式进行了调整。 - - 在标签/多选模式下,选中或删除选项增加了动画效果。 -- Alert - - 默认样式不展示图标。 - - 带描述的警告框图标改为描线图标。 - - `type="warn"` 图标修改。 -- Dropdown 新增带菜单触发的按钮 `Dropdown.Button`。[演示](http://ant.design/components/dropdown/#demo-dropdown-button) -- Menu - - 新增 `Menu.ItemGroup` 用于把菜单项分组。 - - onOpen 和 onClose 函数的参数新增了 `keyPath` 数据,可用于制作手风琴类型的菜单。 -- Badge - - 徽章可以独立使用。[演示](http://ant.design/components/badge/#demo-no-wrapper) - - 支持设置封顶的 `99+` 的数字。[演示](http://ant.design/components/badge/#demo-overflow) -- Slider - - 增加 `onAfterChange` 事件。[演示](http://ant.design/components/slider/#demo-event) - - 现在设置 `tipFormatter={null}` 可以隐藏 `Tooltip`。 - - 双滑块拖动体验优化,一个滑块在拖动时可以直接跨过另一滑块。 -- Breadcrumb 可以自定义分隔符。[演示](http://ant.design/components/breadcrumb/#demo-separator) -- Popconfirm 添加 `visible` 属性,使其可以控制是否显示。[演示](http://ant.design/components/popconfirm/#demo-dynamic-trigger) -- 修复 Icon `ref` 引起的报错。 -- 修复 Calendar 组件无法切换年/月的问题。[#757](https://github.com/ant-design/ant-design/issues/757) -- Checkbox 新增 `Checkbox.Group`,现可以方便的 [生成一组选择框](http://ant.design/components/checkbox/#demo-group) -- Tabs - - 新增 [卡片式页签](http://ant.design/components/tabs/#demo-card)。 - - 调整 [新增和关闭页签](http://ant.design/components/tabs/#demo-editable-card) 的样式。 - - 现在支持页签的四个位置 `tabPosition="top|right|bottom|left"`。 - - 移除 `animation` 属性,并在 `tabPosition="top|bottom"` 时默认启用切换动画。 -- Timepicker - - **重命名为 TimePicker。** - - 新增 `value` 属性。 - - 新增属性 `disabledHours` `disabledMinutes` `disabledSeconds`。[演示](http://ant.design/components/time-picker/#picker-demo-disable-options) - - 移除 `hourOptions` `minuteOptions` `secondOptions`,新增 `hideDisabled` 属性用于替代。 -- Datepicker - - **重命名为 DatePicker。** - - 新增 [日期范围选择控件](http://ant.design/components/date-picker/#picker-demo-range)。 - - 修改 `showTime` 的交互。[演示](http://ant.design/components/date-picker/#picker-demo-time) - - 修正为受控组件。 -- Table - - **移除 `dataSource` 的远程模式。** - - 新增 [紧凑型表格](http://ant.design/components/table/#demo-size)。 - - 允许监听分页的 `onShowSizeChange`。[演示](http://ant.design/components/table/#demo-paging) - - 优化表格对树形数据的显示。[演示](http://ant.design/components/table/#demo-indent-size) - - 优化了筛选菜单的样式,并添加了最大高度。[演示](http://ant.design/components/table/#demo-head)。 - - 修复 column.key 设置失效的问题。[#642](https://github.com/ant-design/ant-design/issues/642) - - 修复设置时 rowKey 时单选会导致全部选中的问题。[#697](https://github.com/ant-design/ant-design/issues/697) - - 修复一个列重新渲染导致选项错乱的问题。[#418](https://github.com/ant-design/ant-design/issues/418#issuecomment-163093580) - - 修复选择列无法设置宽度的问题。[#649](https://github.com/ant-design/ant-design/issues/649) -- Form - - 修复了 Textarea 无法输入的问题。[#646](https://github.com/ant-design/ant-design/issues/646) - - 修复了 Textarea 设置 `cols` 和 `rows` 属性失效的问题。[#694](https://github.com/ant-design/ant-design/issues/694) - - 修复无法设置 `className` 的问题。[#711](https://github.com/ant-design/ant-design/issues/711) -- 修复 Upload 组件在 `beforeUpload` 返回 `false` 后依然更新上传列表问题。[#757](https://github.com/ant-design/ant-design/issues/757) -- 工具 - - 替换 `antd build` 为 [atool-build](https://github.com/ant-tool/atool-build),重构并改善了 webpack 配置的自定义方式。 - - 替换 `antd server` 为 [dora](https://github.com/dora-js/dora),一个完全插件化的开发服务器,支持[代理转发和数据 Mock](https://github.com/dora-js/dora-plugin-proxy)、[atool-build](https://github.com/dora-js/dora-plugin-atool-build)、[热替换](https://github.com/dora-js/dora-plugin-hmr)。 - - 新增 babel 插件 [babel-plugin-antd](https://github.com/ant-design/babel-plugin-antd),转换 `import {Button} from 'antd'` 为 `import Button from 'antd/lib/button'`。 - - 发布了 `antd-init@0.6.x`,支持以上改动。 + - 搜索框和单选选择框合并,以优化视觉和交互效果。 + - 优化多选框的选中效果。 +- Spin + - 增加延时展示以优化体验。[#1273](https://github.com/ant-design/ant-design/issues/1273) + - 增加 `tip` 属性用于定义加载文案。[#1046](https://github.com/ant-design/ant-design/issues/1046) +- Steps + - 重构布局方式,以支持更灵活的自适应布局和优化了性能,并移除了 `maxDescriptionWidth` 属性。[#1099](https://github.com/ant-design/ant-design/issues/1099) + - 新增 `status` 属性以指定当前步骤状态,同时支持错误步骤的展示。[#1098](https://github.com/ant-design/ant-design/issues/1098) +- Timeline + - 新增 `dot` 属性,可自定义时间轴点。 + - 现在可以设置 `className` 和 `style` 的问题。 + - `color` 属性现在支持自定义色值。 +- Tree + - 当子节点被选中时,自动展开父节点。 + - 新增 `checkStrictly` 属性,支持父子节点选中关系脱离。 +- Upload + - 在上传文件列表中的文件被删除时,将触发 `onRemove` 事件。[#1240](https://github.com/ant-design/ant-design/issues/1240) + - 增加 `onPreview` 支持文件的自定义预览方式。[#1240](https://github.com/ant-design/ant-design/issues/1240) + - `data` 属性支持设为一个函数,用于动态修改上传参数。[react-component/upload#32](https://github.com/react-component/upload/pull/32) +- Slider `marks` 现在支持 JSX 并可以单独设置某个标记的样式。 +- Tag 的 `onClose` 可以使用 `e.preventDefault()` 阻止默认事件。[#1267](https://github.com/ant-design/ant-design/issues/1267) +- Form.Item 在有多个 child 时也可以自动生成错误信息与校验状态,但一个 Form.Item 内仍然只能有一个表单控件。[#1287](https://github.com/ant-design/ant-design/issues/1287) +- Input 新增 `onPressEnter` 属性监听回车事件。 +- Table 现在可以通过 `filteredValue` `sortOrder` 控制筛选和排序的状态。[#971](https://github.com/ant-design/ant-design/issues/971) +- Button 增加了 `icon` 属性。[#1199](https://github.com/ant-design/ant-design/issues/1199) +- SubMenu 增加 `onTitleClick` 属性。 +- Affix 增加 `offsetBottm` 属性,支持固定在底部。[#1000](https://github.com/ant-design/ant-design/issues/1000) -> [0.11 升级指南](http://ant.design/docs/react/upgrade-notes#0-10-gt-0-11) +### 相关工具发布 ---- +- [antd-init](http://github.com/ant-design/antd-init) 同步发布 `1.0.0` 版本,享受最新 [ant-tool](https://github.com/ant-tool/) 工具带来的流畅开发体验。 +- [Ant Motion](http://motion.ant.design) 全新的动效设计解决方案。 +- [Ant UX](http://ux.ant.design/) 发布 1.0 版本,提供多种平台的流程素材支持。 -## 0.10.5 +## 0.12.17 -`2016-01-04` - -- 修复 Table 更新 dataSource 后,选中项没有置空的问题。[#793](https://github.com/ant-design/ant-design/issues/793) - -## 0.10.4 - -`2015-11-30` - -- 将 media-match 加入默认的 polyfill 文件中。[5626974](https://github.com/ant-design/ant-design/commit/562697423b1139eb324c1dceb051c143f4870ed7) -- 修复了 [MonthPicker](http://ant.design/components/datepicker/#demo-month-picker) 报错问题,并增加了相关演示。 -- 修复 RadioGroup 中的 Radio/RadioButton 无法单独设置 disabled 的问题。[#603](https://github.com/ant-design/ant-design/issues/603) -- 修复今天是不可选日期时的一个展示问题。[#606](https://github.com/ant-design/ant-design/issues/606) - - -## 0.10.3 - -`2015-11-26` - -- 和 0.9.x 保持一致默认引入 `antd/lib/index.css`(而非 less 文件),方便第三方引用。引用 less 文件进行变量配置的可自行 `import 'antd/style/index.less'`。[#593](https://github.com/ant-design/ant-design/issues/593) -- 升级 Pagination,增加 `defaultCurrent` 属性,修正原来的 `current` 为[完全受控属性](https://facebook.github.io/react/docs/forms.html#controlled-components)。 -- Pagination 的改动也修复了 Table 切换数据源后回到[第一页的例子](http://ant.design/components/table/#demo-ajax)。 -- 对演示和样式代码增加了 lint 检查。 - - -## 0.10.2 - -`2015-11-25` - -- Slider 新增 `tipFormatter` 用于格式化 Tooltip 的内容。 -- 优化 Badge 动画效果。 -- 修复以下问题: - - 文本域的表单校验无法重置。 - - 设置 Upload 的 `multiple` 为 `true` 时,未显示每个文件的上传进度。 - - Breadcrumb 配合 Router 的时候如果没有 `breadcrumbName` 会抛错。 - - InputNumber 同时设置 `size` `className` 时会有冲突。 - - -## 0.10.1 - -`2015-11-20` - -- 修改内部组件的引用结构,方便工具优化。[#566](https://github.com/ant-design/ant-design/pull/566) - - 移除了演示中没有使用过的 `antd.ButtonGroup`,依然用 `const ButtonGroup = Button.Group` 来使用。 - - Form 和 Input 目录分离,`import { Form, Input } from 'ant/lib/form'` 的引用方式被废弃。 - - 现在可以 `import Form from 'ant/lib/form'` 和 `import Input from 'ant/lib/input'`。 - - 原有的 `import { Form, Input } from 'antd'` 则不受影响。 - -- 修复 Datepicker 的 `style` 和 `calendarStyle` 属性失效的问题,并将 `calendarStyle` 更名为 `popupStyle`。 - - -## 0.10.0 - -`2015-11-20` - -- 全面兼容 `react@0.14.x`。 -- 新增 [时间选择 Timepicker](http://ant.design/components/timepicker/)、[日历 Calendar](http://ant.design/components/calendar/)、[加载中 Spin](http://ant.design/components/spin/) 组件。 -- [Button](http://ant.design/components/button/)、[Iconfont](http://ant.design/components/icon/)、[Layout](http://ant.design/components/layout/)、[Form](http://ant.design/components/form/)、[Input](http://ant.design/components/form/#demo-input) 等样式模块改造为 React 组件。 -- 新增 [Queue-anim](http://ant.design/components/queue-anim/) 组件,更换了原来的 enter-animation。 -- 全新的[字体图标](/components/icon)。 - - 全面更新视觉风格,补充更多图标。[#313](https://github.com/ant-design/ant-design/issues/313) - - 调整字体基线,告别对图标位置的特殊调节。(感谢 [iconfont.cn](http://iconfont.cn) 的鼎力支持) -- Datepicker、Dropdown、Select、Popover、Popconfirm 等浮层组件添加在空间不足的情况下自动调整位置功能。 -- Popover、Tooltip、Popconfirm 组件支持 12 个方向。[#312](https://github.com/ant-design/ant-design/issues/312) -- 优先使用苹方字体。 -- 统一 size 属性的可选值为 `small` `default` `large`。 -- 开始初步补充[测试用例](https://github.com/ant-design/ant-design/tree/1a3a19793c0791201666fdcf0dbd12a30fad4be0/tests)。 -- 提供主色系更换的[方案](https://github.com/ant-tool/xtool/tree/master/examples/customize-antd-theme)。[#384](https://github.com/ant-design/ant-design/issues/384) -- 添加[色彩换算工具](http://ant.design/spec/colors#色彩换算工具)。 -- 添加布局和导航规范,以及[常用布局](http://ant.design/spec/layout/)。 -- 文档支持标题和演示的锚点,方便分享文档和演示代码。 -- 提供多版本的文档,在[主站](http://ant.design)的右下角提供切换按钮。 -- [antd-bin](https://github.com/ant-tool/xtool) 升级到 `0.10`。 - - 拆分出 [antd-init](https://github.com/ant-design/antd-init) 和 [antd-build](https://github.com/ant-design/antd-build)。 - - 提供代理功能。 - - 提供 UI 测试功能。 - -#### 组件变更 - -- Table - - 支持单选。[演示](http://ant.design/components/table/#demo-row-selection-radio-props) - - 选择模式支持默认选中和不可用效果。[演示](/components/table/#demo-row-selection-props) - - 列支持了 `colSpan` 和 `rowSpan` 配置。[演示](/components/table/#demo-colspan-rowspan) - - 新增 `loading` 属性。 - - 筛选增加 `filterMultiple` 属性,支持单选的配置。 -- Datepicker - - 添加国际化支持。 - - 添加手动输入和清除功能。 - - 优化了视觉样式。 - - 修复不标准的日期格式导致显示错误的问题。 - - 用 `onChange` 属性代替 `onSelect` 属性。 -- Validation 修复了 对 Datepicker、Input-number、Select 的支持,并添加了相关演示。 -- Carousel 的依赖 react-slick 升级到 0.9.x,相关 API 也相应更新。 -- Tree 组件支持完全受控模式。[#397](https://github.com/ant-design/ant-design/issues/397) -- Input Number - - 组件输入体验优化,现在可以键入任意字符,失焦时格式化为合法值。 - - 修复不支持小数 step 的问题。[#530](https://github.com/ant-design/ant-design/issues/530) -- Tabs 新增[垂直页签功能](http://ant.design/components/tabs/#demo-vertical-left)。 -- Upload 组件视觉优化,新增高级浏览器下的上传进度展示。[#311](https://github.com/ant-design/ant-design/issues/311) -- Menu - - 视觉效果大幅优化。 - - 新增 [dark 主题](http://ant.design/components/menu/#demo-theme) 的样式。 - - 修复一个链接点击区域的问题。[#535](https://github.com/ant-design/ant-design/issues/535) -- Dropdown 用 onClick 代替 onSelect 作为推荐的使用方式,因为原有的 onSelect 只在变化时触发。 -- Slider - - 新增[双滑块功能](http://ant.design/components/slider/#demo-range)。 - - 优化 marks 属性的使用逻辑,使其可以和具体数值进行绑定。[slider#26](https://github.com//react-component/slider/issues/26) - - 属性命名优化,用 `dots` 代替了 `withDots` 属性,用 `included` 代替了 `isIncluded`。 -- Badge 当 `count` 为 0 时不展示。 -- Progress 新增 `format` 属性,能够自定义展示的进度文案。 -- Modal 新增 `confirmLoading` 属性。 -- 新增 Radio.Button 的失效样式。 -- 提供 IE8 下的圆角按钮[兼容方案](http://ant.design/components/button/#ie8-border-radius-support)。 -- `antd.Notification()` 修正为 `antd.notification()`。 -- 另有巨量样式的修复和优化。 - -> 备注: -> -> - [计划和推进 issue](https://github.com/ant-design/ant-design/issues/276) -> - [0.10 升级指南](http://010x.ant.design/docs/upgrade-to-0.10) - ---- - -## 0.9.3 ~ 0.9.5 - -`2015-11-04` - -* 增加对 React 版本的检测提示机制,0.9.x 序列只能使用 `react@~0.13.3`。 - - -## 0.9.2 - -`2015-10-26` - -* Tooltip 的 title 为空时不展示浮层。[9b53117](https://github.com/ant-design/ant-design/commit/9b5311791e73270c7c16a602ac74dd59719a5f76) -* 修复 Upload 文件列表链接的 target 属性。[340a170](https://github.com/ant-design/ant-design/commit/340a1702b6a7b065ac02d417c891e1886dfe470d) -* 修复 Datepicker 设置 defaultValue 时星期顺序错误的问题。[9ef1450](https://github.com/ant-design/ant-design/commit/9ef14500f3abfcc7081f8dceab8187ec835e3918) -* 修复一些小的样式问题。 - - -## 0.9.1 - -`2015-09-26` - -* 添加 Pagination pageSize 发生变化的回调。[#317](https://github.com/ant-design/ant-design/issues/317) -* 升级依赖 rc-upload 到 1.6.x,修复 IE8/9 下的兼容性问题。 -* 升级依赖 rc-steps 到 1.3.x。 - * 新增 current 属性,方便配置当前的步骤。[#290](https://github.com/ant-design/ant-design/issues/290) - * 修复因滚动条影响页面宽度导致的错位问题。 -* 升级依赖 rc-menu 到 1.5.x。 - * 新增 onSelect 回调中返回参数 keyPath,从而支持只展开当前父级菜单的交互方式。[demo](http://ant.design/components/menu/#demo-sider-current) - * 修复 hover 类型的弹出菜单能响应点击事件的问题。[react-component/menu#14](https://github.com/react-component/menu/issues/14) -* 修复一个 Table 的分页无法正确展示的问题。[#253](https://github.com/ant-design/ant-design/issues/253) -* 修复一个 combobox 选择框无法选中的问题。[0435ca6](https://github.com/ant-design/ant-design/commit/0435ca60e3b574bac3808a10ba3db62f482335fd) -* 修复 Radio.Button 在 IE 8 下不可用的问题。[#321](https://github.com/ant-design/ant-design/issues/321) -* 适配 breadcrumb 面包屑组件和 `react-router@1.0.0-rc1`。 -* 修复只能同时弹出一个 Modal 通知框的问题。[d6a4094](https://github.com/ant-design/ant-design/commit/d6a4094bc4c72acd05be001c7e46dbd17092001a) -* 升级依赖 rc-tooltip 到 2.8.0,增加 overlayClassName 属性。 -* 部分组件交互和视觉效果修正。 - - -## 0.9.0 - -`2015-09-14` - -* 新增 [timeline](components/timeline/) 和 [badge](components/badge/) 组件。 -* 优化弹出层类组件的动画效果,使其更加流畅。 -* 部分文案更新。 -* 优化主站在小分辨率屏幕下的样式。 -* 使用 instantclick 改造主站,加载速度有明显提升。 -* antd-bin 升级到 [0.6.x](https://github.com/ant-design/antd-bin/blob/master/HISTORY.md) 。 -* Upload **重构了 API 接口,不向下兼容**,支持自定义的功能扩展。 - * 新增 `onChange(file) {}` 接口,移出原来的 `onSuccess`、`onProgess`、`onError` 等接口。 - * 新增 `fileList` 和 `defaultFileList` 属性,以满足更多的自定义功能,具体见演示。 - * 设置 fileList 数组项的 url 属性可以作为链接展示在文件列表中方便下载。 - * 移除内建的上传成功或失败的信息提示,业务可自行实现。 - * 修正多文件选择上传时文件列表只展示一个文件的问题。 -* Table - * 新增可展开的 table。[#258](https://github.com/ant-design/ant-design/pull/258) - * 新增无数据的展示样式。[4c54644](https://github.com/ant-design/ant-design/commit/4c54644116d46cb2510d2d475234529bad60e5d5) - * 修复本地模式 `dataSource` 无法更新的问题。[6d2dcc4](https://github.com/ant-design/ant-design/commit/6d2dcc45393b6ec0ad1ba73caf8b1ec42353743f) - * 修复远程模式 loading 失效的问题。[9b8abb2](https://github.com/ant-design/ant-design/commit/9b8abb219934c246970a84200818aa8f85974bdf) - * 用 [reqwest-without-xhr2](http://npmjs.com/reqwest-without-xhr2) 代替了 reqwest,解决某些开发环境下 xhr2 依赖的问题。 -* Select - * 增加 label 属性,允许多选模式下展示标签(原来只能显示 value 值)。[演示](http://react-component.github.io/select/examples/mul-suggest.html) - * 修复 combobox 模式下 value 失效的问题。 -* Notification 修复不会自动消失的问题。[23fce55](https://github.com/ant-design/ant-design/commit/23fce559b0b2faf4e0b686a92dbcdd045727a464) -* Steps 新增竖版的步骤条。 -* Carousel 修复 fade 模式下可以拖拽的问题。#212 -* Collapse 修复动画不生效的问题。 -* Datepicker 修复无法设置为空值的问题。 -* Modal - * 添加 [通知类型](http://ant.design/components/modal/#demo-info) 的对话框函数。 - * 用 `Modal.confirm()` 代替 `confirm()` 方法。 - * 修改为需要在 onCancel 手动设置 visible 属性来关闭。 -* Message 添加 [加载中类型](http://ant.design/components/message/#demo-loading)。 -* Radio 修改 Radio.Group 容器的盒模型属性为 inline-block 。 -* Enter Animation - * 大幅度的重构,全新 API 的设计。 - * 支持和 react-router 结合使用。 - - -## 0.8.0 - -`2015-08-25` - -这个版本是第一个稳定版,组件经过三期迭代,基本到齐,并有大量改进和变化,不向下兼容。 - -* 新增九个组件 `menu`、`upload`、`carousel`、`tree`、`notification`、`validation`、`affix`、`alert`、`enterAnimation`。目前共有组件 34 个,基本能满足后台类项目的组件需求。 -* 新增设计文档部分,包括文字、色彩、动画。 -* 重新梳理了设计和 React 实现部分的关系,强调了 Ant Design 的设计属性,并更新了网站的信息结构。 -* 构建工具 `antd-bin` 升级到 `0.4.0` 版本,支持合并 webpack 配置,热替换(HMR)等特性,[详见](https://github.com/ant-design/antd-bin)。 -* 组件动画优化和补充,体验更加流畅动感。 -* 排查并修复 IE 和 safari 等浏览器的兼容问题。 -* 大量代码重构,演示代码补充,文档更新、以及样式上的优化。 - -## 0.7.3 - -`2015-07-30` - -* 小幅重构了 Table 分页,修复了分页导致的数据不展示的问题。 -* 更新了部分文档。 - -## 0.7.2 - -`2015-07-27` - -* 修复本地模式下 pagination 为 false 时数据无法显示的 [问题](https://github.com/ant-design/ant-design/commit/1954586665e59031eae5d2c8b2cdb08f83d64fcb)。 -* 重构了 message 组件。 -* 添加英文版说明文档 [README-en_US.md](https://github.com/ant-design/ant-design/blob/master/README-en_US.md)。 -* 部分代码切换至 ES6 模式。 -* 修正了部分组件的样式和演示,优化部分动画。 - -## 0.7.1 - -`2015-07-22` - -* 修复了 Table 组件的 pagination 为 false 时分页未消失的 [问题](https://github.com/ant-design/ant-design/commit/01a6c0f1e6707b72a54ef30d073d148a87b391a8)。 -* select 组件[选中后默认显示标签内容](https://github.com/ant-design/ant-design/issues/50)(原来是显示 value)。 -* 修正了部分组件的样式和演示。 -* 打包文件为 [umd 模式](https://github.com/ant-design/ant-design/commit/9b7b940cb417429d8fc57d83e252991b043d0f2f)。 - -## 0.7.0 - -`2015-07-21` - -* 第一个公开版本,发布 `layout`、`iconfont`、`button`、`form`、`checkbox`、`radio`、`switch`、`slider`、`input-number`、`datepicker`、`select`、`tabs`、`steps`、`breadcrumb`、`collapse`、`pagination`、`modal`、`message`、`dropdown`、`popover`、`popconfirm`、`tooltip`、`progress`、`table` 等组件。 -* 发布 [Ant Design 首页](http://ant.design/) 和入门文档。 +去 [GitHub](https://github.com/ant-design/ant-design/releases?after=1.0.0) 查看 `0.12.x` 及之前的更新日志。 diff --git a/LICENSE b/LICENSE index c208f57b6a..12189b627d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT LICENSE -Copyright (c) 2015 Alipay.com, https://www.alipay.com/ +Copyright (c) 2015-present Alipay.com, https://www.alipay.com/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README-zh_CN.md b/README-zh_CN.md index 278d34d632..f07781f6b5 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -11,9 +11,8 @@ ## 特性 - 提炼和服务企业级中后台产品的交互语言和视觉风格。 -- [React Component](http://react-component.github.io/badgeboard/) 上精心封装的高质量 UI 库。 -- 基于 npm + webpack + babel 的工作流,支持 ES2015。 - +- [React Component](http://react-component.github.io/badgeboard/) 基础上精心封装的高质量 UI 组件。 +- 基于 npm + webpack + babel 的工作流,支持 ES2015 和 TypeScript。 ## 安装 @@ -31,7 +30,7 @@ ReactDOM.render(, mountNode); 引入样式: ```jsx -import 'antd/lib/index.css'; // or 'antd/style/index.less' +import 'antd/dist/antd.css'; // or 'antd/dist/antd.less' ``` 按需加载可通过此写法 `import DatePicker from 'antd/lib/date-picker'` 或使用插件 [babel-plugin-antd](https://github.com/ant-design/babel-plugin-antd)。 @@ -43,18 +42,31 @@ import 'antd/lib/index.css'; // or 'antd/style/index.less' > [IE8 issues](https://github.com/xcatliu/react-ie8) +## TypeScript + +```js +/// +... +``` + ## 链接 - [首页](http://ant.design/) -- [React UI 库](http://ant.design/docs/react/introduce) +- [React 实现](http://ant.design/#/docs/react/introduce) - [修改记录](CHANGELOG.md) - [开发脚手架](https://github.com/ant-design/antd-init/) - [开发工具文档](http://ant-tool.github.io/) -- [React 组件](http://react-component.github.io/) +- [React 基础组件](http://react-component.github.io/) +- [移动端组件](http://mobile.ant.design) - [React 代码规范](https://github.com/react-component/react-component.github.io/blob/master/docs/zh-cn/component-code-style.md) - [组件设计原则](https://github.com/react-component/react-component.github.io/blob/master/docs/zh-cn/component-design.md) - [网站和组件开发说明](https://github.com/ant-design/ant-design/wiki/%E7%BD%91%E7%AB%99%E5%92%8C%E7%BB%84%E4%BB%B6%E5%BC%80%E5%8F%91%E8%AF%B4%E6%98%8E) -- [版本发布手册](https://github.com/ant-design/ant-design/wiki/%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B) +- [版本发布手册](https://github.com/ant-design/ant-design/wiki/%E8%BD%AE%E5%80%BC%E8%A7%84%E5%88%99%E5%92%8C%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B) +- [社区贡献脚手架和范例](https://github.com/ant-design/ant-design/issues/129) +- [常见问题](https://github.com/ant-design/ant-design/wiki/FAQ) +- [CodePen 模板](http://codepen.io/anon/pen/wGOWGW?editors=001) +- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design) + ## 如何贡献 diff --git a/README.md b/README.md index fcc11b03a6..fefd553398 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,14 @@ An enterprise-class UI design language and React-based implementation. +## :loudspeaker: Document Translation Recruitment + +We are now working on translate components document to English, and we need some translator and reviewer. https://github.com/ant-design/ant-design/issues/1471 + ## Features -- An enterprise-class design language and high quality UI style. -- Rich library of UI components base on [React Component](http://react-component.github.io/badgeboard/). +- An enterprise-class design language and high quality UI. +- Graceful UI components out of the box, base on [React Component](http://react-component.github.io/badgeboard/). - A npm + webpack + babel + dora [workflow](http://ant-tool.github.io/index.html). ## Install @@ -20,20 +24,44 @@ An enterprise-class UI design language and React-based implementation. npm install antd ``` -## Usage example +## Usage + +### Use prebuilt bundle ```jsx import { DatePicker } from 'antd'; ReactDOM.render(, mountNode); ``` -Import style: +And import style manually: ```jsx -import 'antd/lib/index.css'; // or 'antd/style/index.less' +import 'antd/dist/antd.css'; // or 'antd/dist/antd.less' ``` -Use components on demand by writing as `import DatePicker from 'antd/lib/date-picker'` or use [babel-plugin-antd](https://github.com/ant-design/babel-plugin-antd). +### Use modularized antd + +- Use [babel-plugin-antd](https://github.com/ant-design/babel-plugin-antd) (Recommended) + + ```js + // .babelrc + { + "plugins": [["antd", { style: "css" }]] + } + ``` + + Then you can import components from antd directly. + + ```jsx + // import js and css modularly, parsed by babel-plugin-antd + import { DatePicker } from 'antd'; + ``` + +- Manually import + + ```jsx + import DatePicker from 'antd/lib/date-picker'; // just for js + ``` ## Browser Support @@ -42,21 +70,37 @@ Normal browsers and Internet Explorer 8+. > [IE8 issues](https://github.com/xcatliu/react-ie8) +## TypeScript + +tsconfig.json + +``` +{ + "compilerOptions": { + "moduleResolution": "node", + "jsx": "preserve" + } +} +``` + ## Links - [Home page](http://ant.design/) -- [React UI library](http://ant.design/docs/react/introduce) +- [React UI page](http://ant.design/#/docs/react/introduce) - [ChangeLog](CHANGELOG.md) - [Scaffold tool](https://github.com/ant-design/antd-init/) - [Development tool](http://ant-tool.github.io/) - [React components](http://react-component.github.io/) +- [Mobile UI](http://mobile.ant.design) - [React style guide](https://github.com/react-component/react-component.github.io/blob/master/docs/zh-cn/component-code-style.md) - [React component design guide](https://github.com/react-component/react-component.github.io/blob/master/docs/zh-cn/component-design.md) -- [Developer Instruction](https://github.com/ant-design/ant-design/wiki/%E7%BD%91%E7%AB%99%E5%92%8C%E7%BB%84%E4%BB%B6%E5%BC%80%E5%8F%91%E8%AF%B4%E6%98%8E) -- [Versioning Release Note](https://github.com/ant-design/ant-design/wiki/%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B) +- [Developer Instruction](https://github.com/ant-design/ant-design/wiki/Development) +- [Versioning Release Note](https://github.com/ant-design/ant-design/wiki/%E8%BD%AE%E5%80%BC%E8%A7%84%E5%88%99%E5%92%8C%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B) +- [Boilerplates](https://github.com/ant-design/ant-design/issues/129) - [FAQ](https://github.com/ant-design/ant-design/wiki/FAQ) - +- [CodePen boilerplate](http://codepen.io/anon/pen/wGOWGW?editors=001) for bug reports +- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design) ## Contributing -We welcome all contributions, please submit any ideas as [pull requests](https://github.com/ant-design/ant-design/pulls) or as a [GitHub issue](https://github.com/ant-design/ant-design/issues). +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 a [GitHub issue](https://github.com/ant-design/ant-design/issues). If you'd like to improve code, check out the [Development Instruction](https://github.com/ant-design/ant-design/wiki/Development) and have a good time! :) diff --git a/components/_util/isCssAnimationSupported.tsx b/components/_util/isCssAnimationSupported.tsx new file mode 100644 index 0000000000..45d51bb356 --- /dev/null +++ b/components/_util/isCssAnimationSupported.tsx @@ -0,0 +1,24 @@ +let animation; + +function isCssAnimationSupported() { + if (animation !== undefined) { + return animation; + } + const domPrefixes = 'Webkit Moz O ms Khtml'.split(' '); + const elm = document.createElement('div'); + if (elm.style.animationName !== undefined) { + animation = true; + } + if (animation !== undefined) { + for (let i = 0; i < domPrefixes.length; i++) { + if (elm.style[`${domPrefixes[i]}AnimationName`] !== undefined) { + animation = true; + break; + } + } + } + animation = animation || false; + return animation; +} + +export default isCssAnimationSupported; diff --git a/components/_util/openAnimation.tsx b/components/_util/openAnimation.tsx new file mode 100644 index 0000000000..f2a33ba216 --- /dev/null +++ b/components/_util/openAnimation.tsx @@ -0,0 +1,36 @@ +import cssAnimation from 'css-animation'; + +function animate(node, show, done) { + let height; + return cssAnimation(node, 'ant-motion-collapse', { + start() { + if (!show) { + node.style.height = `${node.offsetHeight}px`; + } else { + height = node.offsetHeight; + node.style.height = 0; + } + }, + active() { + node.style.height = `${show ? height : 0}px`; + }, + end() { + node.style.height = ''; + done(); + }, + }); +} + +const animation = { + enter(node, done) { + return animate(node, true, done); + }, + leave(node, done) { + return animate(node, false, done); + }, + appear(node, done) { + return animate(node, true, done); + }, +}; + +export default animation; diff --git a/components/_util/splitObject.tsx b/components/_util/splitObject.tsx new file mode 100644 index 0000000000..d1061f40b4 --- /dev/null +++ b/components/_util/splitObject.tsx @@ -0,0 +1,12 @@ +export default function splitObject(obj, parts): Array { + let left = {}; + let right = {}; + Object.keys(obj).forEach((k) => { + if (parts.indexOf(k) !== -1) { + left[k] = obj[k]; + } else { + right[k] = obj[k]; + } + }); + return [left, right]; +} diff --git a/components/affix/demo/basic.md b/components/affix/demo/basic.md index 7d0c6a5233..4dadf3e9ef 100644 --- a/components/affix/demo/basic.md +++ b/components/affix/demo/basic.md @@ -1,17 +1,24 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- -- order: 0 +## zh-CN 最简单的用法。 ---- +## en-US + +The simplest usage. ````jsx import { Affix, Button } from 'antd'; ReactDOM.render( - + , mountNode); ```` diff --git a/components/affix/demo/bottom.md b/components/affix/demo/bottom.md new file mode 100644 index 0000000000..f8faf64264 --- /dev/null +++ b/components/affix/demo/bottom.md @@ -0,0 +1,24 @@ +--- +order: 2 +title: + zh-CN: 下方固定 + en-US: Bottom +--- + +## zh-CN + +固定在屏幕下方。 + +## en-US + +Affix to bottom. + +````jsx +import { Affix, Button } from 'antd'; + +ReactDOM.render( + + + +, mountNode); +```` diff --git a/components/affix/demo/offset.md b/components/affix/demo/offset.md index 561c070ab1..878bbf110a 100644 --- a/components/affix/demo/offset.md +++ b/components/affix/demo/offset.md @@ -1,17 +1,24 @@ -# 偏移 +--- +order: 1 +title: + zh-CN: 偏移 + en-US: Offset +--- -- order: 1 +## zh-CN 达到一定的偏移量才触发。 ---- +## en-US + +Affix element according to offset value. ````jsx import { Affix, Button } from 'antd'; ReactDOM.render( - - + + , mountNode); ```` diff --git a/components/affix/demo/on-change.md b/components/affix/demo/on-change.md new file mode 100644 index 0000000000..9b4d005021 --- /dev/null +++ b/components/affix/demo/on-change.md @@ -0,0 +1,24 @@ +--- +order: 3 +title: + zh-CN: 固定状态改变的回调 + en-US: Callback +--- + +## zh-CN + +可以获得是否固定的状态。 + +## en-US + +Callback with affixed state. + +````jsx +import { Affix, Button } from 'antd'; + +ReactDOM.render( + console.log(affixed)}> + + +, mountNode); +```` diff --git a/components/affix/index.en-US.md b/components/affix/index.en-US.md new file mode 100644 index 0000000000..8cd7887a92 --- /dev/null +++ b/components/affix/index.en-US.md @@ -0,0 +1,21 @@ +--- +category: Components +type: Other +english: Affix +--- + +Make an element sticky to viewport. + +## When to use + +When user browses a long web page, some content need to sticky to viewport. It is common for menus and actions. + +Please note that Affix should not cover other content in page, especially when the size of viewport is small. + +## API + +| Property | Description | Type | Default | +|--------------|-----------------------|----------|--------------| +| offsetTop | Pixels to offset from top when calculating position of scroll | Number | 0 | +| offsetBottom | Pixels to offset from bottom when calculating position of scroll | Number | - | +| onChange | Callback when affix state is changed | Function(affixed) | - | diff --git a/components/affix/index.jsx b/components/affix/index.jsx deleted file mode 100644 index 0c8eb35f65..0000000000 --- a/components/affix/index.jsx +++ /dev/null @@ -1,109 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import rcUtil from 'rc-util'; -import classNames from 'classnames'; - -function getScroll(w, top) { - let ret = w[`page${top ? 'Y' : 'X'}Offset`]; - let method = `scroll${top ? 'Top' : 'Left'}`; - if (typeof ret !== 'number') { - let d = w.document; - // ie6,7,8 standard mode - ret = d.documentElement[method]; - if (typeof ret !== 'number') { - // quirks mode - ret = d.body[method]; - } - } - return ret; -} - -function getOffset(element) { - let rect = element.getBoundingClientRect(); - let body = document.body; - let clientTop = element.clientTop || body.clientTop || 0; - let clientLeft = element.clientLeft || body.clientLeft || 0; - let scrollTop = getScroll(window, true); - let scrollLeft = getScroll(window); - - return { - top: rect.top + scrollTop - clientTop, - left: rect.left + scrollLeft - clientLeft - }; -} - -let Affix = React.createClass({ - - getDefaultProps() { - return { - offset: 0 - }; - }, - - propTypes: { - offset: React.PropTypes.number - }, - - getInitialState() { - return { - affix: false, - affixStyle: null - }; - }, - - handleScroll() { - let affix = this.state.affix; - let scrollTop = getScroll(window, true); - let elemOffset = getOffset(ReactDOM.findDOMNode(this)); - - if (!affix && (elemOffset.top - this.props.offset) < scrollTop) { - this.setState({ - affix: true, - affixStyle: { - top: this.props.offset, - left: elemOffset.left, - width: ReactDOM.findDOMNode(this).offsetWidth - } - }); - } - - if (affix && (elemOffset.top - this.props.offset) > scrollTop) { - this.setState({ - affix: false, - affixStyle: null - }); - } - }, - - componentDidMount() { - this.scrollEvent = rcUtil.Dom.addEventListener(window, 'scroll', this.handleScroll); - this.resizeEvent = rcUtil.Dom.addEventListener(window, 'resize', this.handleScroll); - }, - - componentWillUnmount() { - if (this.scrollEvent) { - this.scrollEvent.remove(); - } - if (this.resizeEvent) { - this.resizeEvent.remove(); - } - }, - - render() { - const className = classNames({ - [this.props.className]: this.props.className, - 'ant-affix': this.state.affix - }); - - return ( -
-
- {this.props.children} -
-
- ); - } - -}); - -module.exports = Affix; diff --git a/components/affix/index.tsx b/components/affix/index.tsx new file mode 100644 index 0000000000..1bd409d4f9 --- /dev/null +++ b/components/affix/index.tsx @@ -0,0 +1,192 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import addEventListener from 'rc-util/lib/Dom/addEventListener'; +import classNames from 'classnames'; +import warning from 'warning'; +import assign from 'object-assign'; +import shallowequal from 'shallowequal'; + +function getScroll(w, top?: boolean) { + let ret = w[`page${top ? 'Y' : 'X'}Offset`]; + const method = `scroll${top ? 'Top' : 'Left'}`; + if (typeof ret !== 'number') { + const d = w.document; + // ie6,7,8 standard mode + ret = d.documentElement[method]; + if (typeof ret !== 'number') { + // quirks mode + ret = d.body[method]; + } + } + return ret; +} + +function getOffset(element) { + const rect = element.getBoundingClientRect(); + const body = document.body; + const clientTop = element.clientTop || body.clientTop || 0; + const clientLeft = element.clientLeft || body.clientLeft || 0; + const scrollTop = getScroll(window, true); + const scrollLeft = getScroll(window); + + return { + top: rect.top + scrollTop - clientTop, + left: rect.left + scrollLeft - clientLeft, + }; +} + +// Affix +export interface AffixProps { + /** + * 距离窗口顶部达到指定偏移量后触发 + */ + offsetTop?: number; + offset?: number; + offsetBottom?: number; + style?: React.CSSProperties; + onChange?: (affixed?: boolean) => any; +} + +export default class Affix extends React.Component { + static propTypes = { + offsetTop: React.PropTypes.number, + offsetBottom: React.PropTypes.number, + }; + + static defaultProps = { + onChange() {}, + }; + + scrollEvent: any; + resizeEvent: any; + refs: { + [key: string]: any; + fixedNode: HTMLElement; + }; + + constructor(props) { + super(props); + this.state = { + affixStyle: null, + placeholderStyle: null, + }; + } + + setAffixStyle(e, affixStyle) { + const originalAffixStyle = this.state.affixStyle; + if (e.type === 'scroll' && originalAffixStyle && affixStyle) { + return; + } + if (shallowequal(affixStyle, originalAffixStyle)) { + return; + } + this.setState({ affixStyle }, () => { + const affixed = !!this.state.affixStyle; + if ((affixStyle && !originalAffixStyle) || + (!affixStyle && originalAffixStyle)) { + this.props.onChange(affixed); + } + }); + } + + setPlaceholderStyle(e, placeholderStyle) { + const originalPlaceholderStyle = this.state.placeholderStyle; + if (e.type === 'resize') { + return; + } + if (shallowequal(placeholderStyle, originalPlaceholderStyle)) { + return; + } + this.setState({ placeholderStyle }); + } + + handleScroll = (e) => { + let { offsetTop, offsetBottom, offset } = this.props; + + // Backwards support + offsetTop = offsetTop || offset; + const scrollTop = getScroll(window, true); + const affixNode = ReactDOM.findDOMNode(this) as HTMLElement; + const fixedNode = ReactDOM.findDOMNode(this.refs.fixedNode) as HTMLElement; + const elemOffset = getOffset(affixNode); + const elemSize = { + width: fixedNode.offsetWidth, + height: fixedNode.offsetHeight, + }; + + const offsetMode = { + top: null as boolean, + bottom: null as boolean, + }; + if (typeof offsetTop !== 'number' && typeof offsetBottom !== 'number') { + offsetMode.top = true; + offsetTop = 0; + } else { + offsetMode.top = typeof offsetTop === 'number'; + offsetMode.bottom = typeof offsetBottom === 'number'; + } + + if (scrollTop > elemOffset.top - offsetTop && offsetMode.top) { + // Fixed Top + this.setAffixStyle(e, { + position: 'fixed', + top: offsetTop, + left: elemOffset.left, + width: affixNode.offsetWidth, + }); + this.setPlaceholderStyle(e, { + width: affixNode.offsetWidth, + height: affixNode.offsetHeight, + }); + } else if (scrollTop < elemOffset.top + elemSize.height + offsetBottom - window.innerHeight && + offsetMode.bottom) { + // Fixed Bottom + this.setAffixStyle(e, { + position: 'fixed', + bottom: offsetBottom, + left: elemOffset.left, + width: affixNode.offsetWidth, + }); + this.setPlaceholderStyle(e, { + width: affixNode.offsetWidth, + height: affixNode.offsetHeight, + }); + } else { + this.setAffixStyle(e, null); + this.setPlaceholderStyle(e, null); + } + } + + componentDidMount() { + warning(!('offset' in this.props), '`offset` prop of Affix is deprecated, use `offsetTop` instead.'); + this.scrollEvent = addEventListener(window, 'scroll', this.handleScroll); + this.resizeEvent = addEventListener(window, 'resize', this.handleScroll); + } + + componentWillUnmount() { + if (this.scrollEvent) { + this.scrollEvent.remove(); + } + if (this.resizeEvent) { + this.resizeEvent.remove(); + } + } + + render() { + const className = classNames({ + 'ant-affix': this.state.affixStyle, + }); + + const props = assign({}, this.props); + delete props.offsetTop; + delete props.offsetBottom; + + return ( +
+
+ {this.props.children} +
+
+ ); + } +} diff --git a/components/affix/index.md b/components/affix/index.zh-CN.md similarity index 57% rename from components/affix/index.md rename to components/affix/index.zh-CN.md index 89c2af0c05..1ac87fcf2e 100644 --- a/components/affix/index.md +++ b/components/affix/index.zh-CN.md @@ -1,8 +1,8 @@ -# Affix - -- category: Components -- chinese: 固钉 - +--- +category: Components +chinese: 固钉 +type: Other +english: Affix --- 将页面元素钉在可视范围。 @@ -18,4 +18,6 @@ | 成员 | 说明 | 类型 | 默认值 | |-------------|----------------|--------------------|--------------| -| offset | 达到指定偏移量后触发 | Number | 0 | +| offsetTop | 距离窗口顶部达到指定偏移量后触发 | Number | | +| offsetBottom | 距离窗口底部达到指定偏移量后触发 | Number | | +| onChange | 固定状态改变时触发的回调函数 | Function(affixed) | 无 | diff --git a/components/affix/style/index.less b/components/affix/style/index.less new file mode 100644 index 0000000000..5d4cdabcd0 --- /dev/null +++ b/components/affix/style/index.less @@ -0,0 +1,6 @@ +@import "../../style/themes/default"; + +.ant-affix { + position: fixed; + z-index: @zindex-affix; +} diff --git a/components/affix/style/index.tsx b/components/affix/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/affix/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/alert/demo/banner.md b/components/alert/demo/banner.md new file mode 100644 index 0000000000..35d3e7f20e --- /dev/null +++ b/components/alert/demo/banner.md @@ -0,0 +1,26 @@ +--- +order: 6 +title: + zh-CN: 顶部公告 + en-US: Banner +--- + +## zh-CN + +用作顶部公告时,默认有图标,`type` 为 'warning',并有特殊样式。 + +## en-US + +When `Alert` is used as banner, it has particular style, Icon and `type`(warning) are specified by default. + +````jsx +import { Alert } from 'antd'; + +ReactDOM.render( +
+ +
+ +
+, mountNode); +```` diff --git a/components/alert/demo/basic.md b/components/alert/demo/basic.md index f0e8d32132..02b0e57b58 100644 --- a/components/alert/demo/basic.md +++ b/components/alert/demo/basic.md @@ -1,14 +1,22 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- -- order: 0 +## zh-CN 最简单的用法,适用于简短的警告提示。 ---- +## en-US + +The simplest usage for short messages. ````jsx import { Alert } from 'antd'; -ReactDOM.render( +ReactDOM.render( + , mountNode); ```` diff --git a/components/alert/demo/closable.md b/components/alert/demo/closable.md index 3046e198e8..d1f35acb71 100644 --- a/components/alert/demo/closable.md +++ b/components/alert/demo/closable.md @@ -1,27 +1,36 @@ -# 可关闭的警告提示 +--- +order: 2 +title: + zh-CN: 可关闭的警告提示 + en-US: Closable +--- -- order: 2 +## zh-CN 显示关闭按钮,点击可关闭警告提示。 ---- +## en-US + +To show close button. ````jsx import { Alert } from 'antd'; const onClose = function (e) { - console.log(e, '我要被关闭啦!'); + console.log(e, 'I was closed.'); }; ReactDOM.render(
- - + + onClose={onClose} + />
, mountNode); ```` diff --git a/components/alert/demo/close-text.md b/components/alert/demo/close-text.md index dc60483b69..4959b2034d 100644 --- a/components/alert/demo/close-text.md +++ b/components/alert/demo/close-text.md @@ -1,15 +1,22 @@ -# 自定义关闭 +--- +order: 5 +title: + zh-CN: 自定义关闭 + en-US: Customized Close Text +--- -- order: 5 +## zh-CN 可以自定义关闭,自定义的文字会替换原先的关闭 `Icon`。 ---- +## en-US + +Replace the default icon with customized text. ````jsx import { Alert } from 'antd'; ReactDOM.render( - + , mountNode); ```` diff --git a/components/alert/demo/description.md b/components/alert/demo/description.md index 80d17dffe7..9d8c5fd189 100644 --- a/components/alert/demo/description.md +++ b/components/alert/demo/description.md @@ -1,28 +1,41 @@ -# 含有辅助性文字介绍 +--- +order: 3 +title: + zh-CN: 含有辅助性文字介绍 + en-US: Description +--- -- order: 3 +## zh-CN 含有辅助性文字介绍的警告提示。 ---- +## en-US + +Additional description for alert message. ````jsx import { Alert } from 'antd'; ReactDOM.render(
- - + message="Success Text" + description="Success Description Success Description Success Description" + type="success" + /> + message="Info Text" + description="Info Description Info Description Info Description Info Description" + type="info" + /> + +
, mountNode); ```` diff --git a/components/alert/demo/icon.md b/components/alert/demo/icon.md index e743140f46..a6fda9ec68 100644 --- a/components/alert/demo/icon.md +++ b/components/alert/demo/icon.md @@ -1,10 +1,17 @@ -# 图标 +--- +order: 4 +title: + zh-CN: 图标 + en-US: Icon +--- -- order: 4 +## zh-CN 可口的图标让信息类型更加醒目。 ---- +## en-US + +Decent icon make information more clear and more friendly. ````jsx import { Alert } from 'antd'; @@ -12,25 +19,31 @@ import { Alert } from 'antd'; ReactDOM.render(
- + - - + + showIcon + /> + type="warning" + showIcon + /> + showIcon + />
, mountNode); ```` diff --git a/components/alert/demo/style.md b/components/alert/demo/style.md index b5400b716d..d5bb487956 100644 --- a/components/alert/demo/style.md +++ b/components/alert/demo/style.md @@ -1,19 +1,25 @@ -# 四种样式 - -- order: 1 - -共有四种样式`success`、`info`、`warn`、`error`。 - --- +order: 1 +title: + zh-CN: 四种样式 + en-US: More types +--- + +## zh-CN + +共有四种样式 `success`、`info`、`warning`、`error`。 + +## en-US + +There are 4 kinds of Alert: `success`, `info`, `warning`, `error`. ````jsx import { Alert } from 'antd'; ReactDOM.render(
- - - - -
, -mountNode); + + + + +, mountNode); ```` diff --git a/components/alert/index.en-US.md b/components/alert/index.en-US.md new file mode 100644 index 0000000000..c52c7d917f --- /dev/null +++ b/components/alert/index.en-US.md @@ -0,0 +1,25 @@ +--- +category: Components +type: Views +english: Alert +--- + +Alert component for feedback. + +## When to use + +- When you need to show alert messages for users. +- When you need a persist static container, and closable by user actions. + +## API + +| Property | Description | Type | Default | +|----------- |--------------------------------------------------------- | ---------- |-------| +| type | Type of Alert styles, options:`success`, `info`, `warning`, `error` | String | `info` | +| closable | Whether Alert can be closed | Boolean | - | +| closeText | Close text to show | React.Node | - | +| message | Content of Alert | React.Node | - | +| description | Additional content of Alert | React.Node | - | +| onClose | Callback when close Alert | Function | - | +| showIcon | Whether to show icon | Boolean | false | +| banner | Whether to show as banner | Boolean | false | diff --git a/components/alert/index.md b/components/alert/index.md deleted file mode 100644 index 3ea71340a7..0000000000 --- a/components/alert/index.md +++ /dev/null @@ -1,26 +0,0 @@ -# Alert - -- category: Components -- chinese: 警告提示 -- type: 展示 - ---- - -警告提示,展现需要关注的信息。 - -## 何时使用 - -- 当某个页面需要向用户显示警告的信息时。 -- 非浮层的静态展现形式,始终展现,不会自动消失,用户可以点击关闭。 - -## API - -| 参数 | 说明 | 类型 | 默认值 | -|----------- |--------------------------------------------------------- | ---------- |-------| -| type | 必选参数,指定警告提示的样式,有四种选择`success`、`info`、`warn`、`error` | String | 无 | -| closable | 可选参数,默认不显示关闭按钮 | Boolean | 无 | -| closeText | 可选参数,自定义关闭按钮 | React.Node | 无 | -| message | 必选参数,警告提示内容 | React.Node | 无 | -| description | 可选参数,警告提示的辅助性文字介绍 | React.Node | 无 | -| onClose | 可选参数,关闭时触发的回调函数 | Function | 无 | -| showIcon | 可选参数,是否显示辅助图标 | Boolean | false | diff --git a/components/alert/index.jsx b/components/alert/index.tsx similarity index 55% rename from components/alert/index.jsx rename to components/alert/index.tsx index 566718731e..680a504294 100644 --- a/components/alert/index.jsx +++ b/components/alert/index.tsx @@ -1,47 +1,74 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; import Animate from 'rc-animate'; import Icon from '../icon'; import classNames from 'classnames'; -export default React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-alert', - showIcon: false, - onClose() {} - }; - }, - getInitialState() { - return { +interface AlertProps { + /** + * Type of Alert styles, options:`success`, `info`, `warning`, `error` + */ + type?: 'success' | 'info' | 'warning' | 'error'; + /** Whether Alert can be closed */ + closable?: boolean; + /** Close text to show */ + closeText?: React.ReactNode; + /** Content of Alert */ + message: React.ReactNode; + /** Additional content of Alert */ + description?: React.ReactNode; + /** Callback when close Alert */ + onClose?: (event) => void; + /** Whether to show icon */ + showIcon?: boolean; + style?: React.CSSProperties; + prefixCls?: string; + banner?: boolean; +} + +export default class Alert extends React.Component { + static defaultProps = { + prefixCls: 'ant-alert', + showIcon: false, + onClose() {}, + type: 'info', + }; + constructor(props) { + super(props); + this.state = { closing: true, - closed: false + closed: false, }; - }, - handleClose(e) { + } + handleClose = (e) => { e.preventDefault(); - let dom = ReactDOM.findDOMNode(this); + let dom = ReactDOM.findDOMNode(this) as HTMLElement; dom.style.height = `${dom.offsetHeight}px`; // Magic code // 重复一次后才能正确设置 height dom.style.height = `${dom.offsetHeight}px`; this.setState({ - closing: false + closing: false, }); - this.props.onClose.call(this, e); - }, - animationEnd() { + this.props.onClose(e); + } + animationEnd = () => { this.setState({ closed: true, - closing: true + closing: true, }); - }, + } render() { let { - closable, description, type, prefixCls, message, closeText, showIcon + closable, description, type, prefixCls, message, closeText, showIcon, banner, } = this.props; + // banner模式默认有 Icon + showIcon = showIcon || banner; + // banner模式默认为警告 + type = banner ? 'warning' : type; + let iconType = ''; switch (type) { case 'success': @@ -51,9 +78,9 @@ export default React.createClass({ iconType = 'info-circle'; break; case 'error': - iconType = 'exclamation-circle'; + iconType = 'cross-circle'; break; - case 'warn': + case 'warning': iconType = 'exclamation-circle'; break; default: @@ -71,6 +98,7 @@ export default React.createClass({ [`${prefixCls}-close`]: !this.state.closing, [`${prefixCls}-with-description`]: !!description, [`${prefixCls}-no-icon`]: !showIcon, + [`${prefixCls}-banner`]: !!banner, }); // closeable when closeText is assigned @@ -81,8 +109,9 @@ export default React.createClass({ return this.state.closed ? null : ( + transitionName={`${prefixCls}-slide-up`} + onEnd={this.animationEnd} + >
{showIcon ? : null} {message} @@ -94,4 +123,4 @@ export default React.createClass({ ); } -}); +} diff --git a/components/alert/index.zh-CN.md b/components/alert/index.zh-CN.md new file mode 100644 index 0000000000..07d6543b7c --- /dev/null +++ b/components/alert/index.zh-CN.md @@ -0,0 +1,27 @@ +--- +category: Components +chinese: 警告提示 +type: Views +english: Alert +--- + +警告提示,展现需要关注的信息。 + +## 何时使用 + +- 当某个页面需要向用户显示警告的信息时。 +- 非浮层的静态展现形式,始终展现,不会自动消失,用户可以点击关闭。 + +## API + +| 参数 | 说明 | 类型 | 默认值 | +|----------- |--------------------------------------------------------- | ---------- |-------| +| type | 指定警告提示的样式,有四种选择 `success`、`info`、`warning`、`error` | String | `info` | +| closable | 默认不显示关闭按钮 | Boolean | 无 | +| closeText | 自定义关闭按钮 | React.Node | 无 | +| message | 警告提示内容 | React.Node | 无 | +| description | 警告提示的辅助性文字介绍 | React.Node | 无 | +| onClose | 关闭时触发的回调函数 | Function | 无 | +| showIcon | 是否显示辅助图标 | Boolean | false | +| banner | 是否用作顶部公告 | Boolean | false | + diff --git a/style/components/alert.less b/components/alert/style/index.less similarity index 71% rename from style/components/alert.less rename to components/alert/style/index.less index 4530fbfcc8..56660b1760 100644 --- a/style/components/alert.less +++ b/components/alert/style/index.less @@ -1,22 +1,25 @@ -@import "../mixins/index"; +@import "../../style/themes/default"; -@alert-prefix-cls: ~"@{css-prefix}alert"; -@alert-title-prefix-cls: ~"@{css-prefix}alert-with-description"; +@alert-prefix-cls: ant-alert; .@{alert-prefix-cls} { position: relative; - padding: 8px 8px 8px 16px; + padding: 8px 48px 8px 38px; border-radius: @border-radius-base; color: @text-color; font-size: 12px; line-height: 16px; margin-bottom: 10px; + &&-no-icon { + padding: 8px 48px 8px 16px; + } + &-icon { - margin-right: 8px; font-size: 14px; - top: 1px; - position: relative; + top: 9px; + left: 16px; + position: absolute; } &-description { @@ -42,7 +45,7 @@ } } - &-warn { + &-warning { border: 1px solid tint(@warning-color, 80%); background-color: tint(@warning-color, 90%); .@{alert-prefix-cls}-icon { @@ -62,8 +65,7 @@ font-size: 12px; position: absolute; right: 16px; - top: 50%; - margin-top: -6px; + top: 10px; height: 12px; line-height: 12px; overflow: hidden; @@ -122,14 +124,49 @@ display: block; } - &-close { - height: 0; - opacity: 0; + &&-close { + height: 0 !important; margin: 0; padding-top: 0; padding-bottom: 0; - transition: all .3s @ease-in-out; + transition: all .3s @ease-in-out-circ; transform-origin: 50% 0; - animation-timing-function: @ease-in-out !important; + } + + &-slide-up-leave { + animation: antAlertSlideUpOut .3s @ease-in-out-circ; + animation-fill-mode: both; + } + + &-banner { + border-radius: 0; + border: 0; + margin-bottom: 0; + } +} + +@keyframes antAlertSlideUpIn { + 0% { + opacity: 0; + transform-origin: 0% 0%; + transform: scaleY(0); + } + 100% { + opacity: 1; + transform-origin: 0% 0%; + transform: scaleY(1); + } +} + +@keyframes antAlertSlideUpOut { + 0% { + opacity: 1; + transform-origin: 0% 0%; + transform: scaleY(1); + } + 100% { + opacity: 0; + transform-origin: 0% 0%; + transform: scaleY(0); } } diff --git a/components/alert/style/index.tsx b/components/alert/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/alert/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/auto-complete/demo/basic.md b/components/auto-complete/demo/basic.md new file mode 100644 index 0000000000..eded8408bb --- /dev/null +++ b/components/auto-complete/demo/basic.md @@ -0,0 +1,45 @@ +--- +order: 0 +title: + zh-CN: 基本使用 + en-US: Basic Usage +--- + +## zh-CN + +基本使用。通过 dataSource 设置自动完成的数据源 + +## en-US + +Basic Usage, set datasource of autocomplete with `dataSource` property. + +````jsx +import { AutoComplete } from 'antd'; + +const Complete = React.createClass({ + getInitialState() { + return { + dataSource: [], + }; + }, + handleChange(value) { + this.setState({ + dataSource: [ + value, + value + value, + value + value + value, + ], + }); + }, + render() { + const { dataSource } = this.state; + return (); + }, +}); + +ReactDOM.render(, mountNode); +```` diff --git a/components/auto-complete/demo/options.md b/components/auto-complete/demo/options.md new file mode 100644 index 0000000000..34d8d5ff5f --- /dev/null +++ b/components/auto-complete/demo/options.md @@ -0,0 +1,50 @@ +--- +order: 2 +title: + zh-CN: 自定义选项 + en-US: Customized +--- + +## zh-CN + +Datasource 的每一项是一个 `AutoComplete.Option`。通过 `AutoComplete.Option` 自定义下拉菜单。 + +## en-US + +Items in dataSource could be an `AutoComplete.Option`. + + +````jsx +import { AutoComplete } from 'antd'; +const Option = AutoComplete.Option; + +const Complete = React.createClass({ + getInitialState() { + return { + dataSource: [], + }; + }, + handleChange(value) { + let dataSource; + if (!value || value.indexOf('@') >= 0) { + dataSource = []; + } else { + dataSource = ['gmail.com', '163.com', 'qq.com'].map((domain) => { + const email = `${value}@${domain}`; + return ; + }); + } + this.setState({ dataSource }); + }, + render() { + const { dataSource } = this.state; + return (); + }, +}); + +ReactDOM.render(, mountNode); +```` diff --git a/components/auto-complete/index.en-US.md b/components/auto-complete/index.en-US.md new file mode 100644 index 0000000000..88df0d84c7 --- /dev/null +++ b/components/auto-complete/index.en-US.md @@ -0,0 +1,30 @@ +--- +category: Components +chinese: 自动完成 +type: Form Controls +cols: 1 +english: AutoComplete +--- + +Autocomplete function of input field. + +## When to use + +When need to use autocomplete function. + +## API + +```jsx +const dataSource = ['12345', '23456', '34567']; + +``` + + +| Property | Description | Type | Default | +|----------------|----------------------------------|------------|--------| +| dataSource | Data source for autocomplete | Array | | +| value | selcted option | String/Array/{key: String, label: React.Node}/Array<{key, label}> | - | +| defaultValue | Initial selected option. | string/Array | - | +| allowClear | Show clear button, effective in multiple mode only. | boolean | false | +| onChange | Called when select an option or input value change, or value of input is changed in combobox mode | function(value, label) | - | +| disabled | Whether disabled select | boolean | false | diff --git a/components/auto-complete/index.tsx b/components/auto-complete/index.tsx new file mode 100644 index 0000000000..063ce0092f --- /dev/null +++ b/components/auto-complete/index.tsx @@ -0,0 +1,71 @@ +import * as React from 'react'; +import Select, { Option, OptGroup } from '../select'; +import classNames from 'classnames'; + +export interface AutoCompleteProps { + size?: string; + className?: string; + notFoundContent?: Element; + dataSource: Array; + prefixCls?: string; + transitionName?: string; + optionLabelProp?: string; + choiceTransitionName?: string; + showSearch?: boolean; +} + +export default class AutoComplete extends React.Component { + static Option = Option; + static OptGroup = OptGroup; + + static defaultProps = { + prefixCls: 'ant-select', + transitionName: 'slide-up', + optionLabelProp: 'children', + choiceTransitionName: 'zoom', + showSearch: false, + }; + + static contextTypes = { + antLocale: React.PropTypes.object, + }; + + render() { + let { + size, className, notFoundContent, prefixCls, optionLabelProp, dataSource, + } = this.props; + + const cls = classNames({ + [`${prefixCls}-lg`]: size === 'large', + [`${prefixCls}-sm`]: size === 'small', + [className]: !!className, + [`${prefixCls}-show-search`]: true, + }); + + const options = dataSource ? dataSource.map((item, index) => { + switch (typeof item) { + case 'string': + return ; + case 'object': + if (React.isValidElement(item)) { + return React.cloneElement(item, { + key: item.key || index, + }); + } + return ; + default: + return []; + } + }) : []; + + return ( + + ); + } +} diff --git a/components/auto-complete/index.zh-CN.md b/components/auto-complete/index.zh-CN.md new file mode 100644 index 0000000000..eab27c5c28 --- /dev/null +++ b/components/auto-complete/index.zh-CN.md @@ -0,0 +1,30 @@ +--- +category: Components +chinese: 自动完成 +type: Form Controls +cols: 1 +english: AutoComplete +--- + +输入框自动完成功能。 + +## 何时使用 + +需要自动完成时。 + +## API + +```jsx +const dataSource = ['12345', '23456', '34567']; + +``` + + +| 参数 | 说明 | 类型 | 默认值 | +|----------------|----------------------------------|------------|---------| +| dataSource | 自动完成的数据源 | Array | | +| value | 指定当前选中的条目 | String/Array/{key: String, label: React.Node}/Array<{key, label}> | 无 | +| defaultValue | 指定默认选中的条目 | String/Array/{key: String, label: React.Node}/Array<{key, label}> | 无 | +| allowClear | 支持清除, 单选模式有效 | boolean | false | +| onChange | 选中 option,或 input 的 value 变化(combobox 模式下)时,调用此函数 | function(value) | 无 | +| disabled | 是否禁用 | boolean | false | \ No newline at end of file diff --git a/components/auto-complete/style/index.less b/components/auto-complete/style/index.less new file mode 100644 index 0000000000..8401f0d117 --- /dev/null +++ b/components/auto-complete/style/index.less @@ -0,0 +1,2 @@ +@import "../../style/themes/default"; +@import "../../select/style/index"; \ No newline at end of file diff --git a/components/auto-complete/style/index.tsx b/components/auto-complete/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/auto-complete/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/back-top/demo/basic.md b/components/back-top/demo/basic.md new file mode 100644 index 0000000000..80bf3a146c --- /dev/null +++ b/components/back-top/demo/basic.md @@ -0,0 +1,25 @@ +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- + +## zh-CN + +最简单的用法。 + +## en-US + +The most basic usage. + +````jsx +import { BackTop } from 'antd'; + +ReactDOM.render( +
+ + 向下滚动后,见右下角灰色按钮 +
+, mountNode); +```` diff --git a/components/back-top/demo/custom.md b/components/back-top/demo/custom.md new file mode 100644 index 0000000000..168db769ab --- /dev/null +++ b/components/back-top/demo/custom.md @@ -0,0 +1,39 @@ +--- +order: 1 +title: + zh-CN: 自定义样式 + en-US: Custom style +--- + +## zh-CN + +可以自定义回到顶部按钮的样式,限制宽高:`40px * 40px`。 + +## en-US + +You can customize the style of the button, just note the size limit: no more than `40px * 40px`. + + +````jsx +import { BackTop } from 'antd'; + +const style = { + height: 40, + width: 40, + lineHeight: '40px', + borderRadius: 4, + backgroundColor: '#57c5f7', + color: '#fff', + textAlign: 'center', + fontSize: 20, +}; + +ReactDOM.render( +
+ +
UP
+
+ 向下滚动后,见右下角蓝色按钮 +
+, mountNode); +```` diff --git a/components/back-top/index.en-US.md b/components/back-top/index.en-US.md new file mode 100644 index 0000000000..91a54eea1b --- /dev/null +++ b/components/back-top/index.en-US.md @@ -0,0 +1,25 @@ +--- +category: Components +type: Other +title: BackTop +--- + +`BackTop` makes it easy to go back to the top of the page. + +## When To Use + +- When the page content is very long. +- When you need to go back to the top very frequently in order to view the contents. + +## API + +> The distance to the bottom is set to `50px` by default, which is overridable. + +> If you decide to use custom styles, please note the size limit: no more than `40px * 40px`. + + +Property | Description | Type | Default +-----|-----|-----|------ +visibilityHeight | the `BackTop` button will not show until the scroll height reaches this value | Number | 400 +onClick | a callback function, which can be executed when you click the button | Function | - + diff --git a/components/back-top/index.tsx b/components/back-top/index.tsx new file mode 100644 index 0000000000..111de53212 --- /dev/null +++ b/components/back-top/index.tsx @@ -0,0 +1,110 @@ +import * as React from 'react'; +import Animate from 'rc-animate'; +import Icon from '../icon'; +import addEventListener from 'rc-util/lib/Dom/addEventListener'; +import classNames from 'classnames'; +import omit from 'object.omit'; + +function getScroll(w, top) { + let ret = w[`page${top ? 'Y' : 'X'}Offset`]; + const method = `scroll${top ? 'Top' : 'Left'}`; + if (typeof ret !== 'number') { + const d = w.document; + // ie6,7,8 standard mode + ret = d.documentElement[method]; + if (typeof ret !== 'number') { + // quirks mode + ret = d.body[method]; + } + } + return ret; +} + +interface BackTopProps { + visibilityHeight?: number; + onClick?: (event) => void; + prefixCls?: string; + className?: string; +} + +export default class BackTop extends React.Component { + static defaultProps = { + onClick() {}, + visibilityHeight: 400, + prefixCls: 'ant-back-top', + }; + + scrollEvent: any; + + constructor(props) { + super(props); + const scrollTop = getScroll(window, true); + this.state = { + visible: scrollTop > this.props.visibilityHeight, + }; + } + + scrollToTop = (e) => { + if (e) { + e.preventDefault(); + } + this.setScrollTop(0); + this.props.onClick(e); + } + + setScrollTop(value) { + document.body.scrollTop = value; + document.documentElement.scrollTop = value; + } + + handleScroll = () => { + const scrollTop = getScroll(window, true); + this.setState({ + visible: scrollTop > this.props.visibilityHeight, + }); + } + + componentDidMount() { + this.scrollEvent = addEventListener(window, 'scroll', this.handleScroll); + } + + componentWillUnmount() { + if (this.scrollEvent) { + this.scrollEvent.remove(); + } + } + + render() { + const { prefixCls, className, children } = this.props; + const classString = classNames({ + [prefixCls]: true, + [className]: !!className, + }); + + const defaultElement = ( +
+ +
+ ); + + // fix https://fb.me/react-unknown-prop + const divProps = omit(this.props, [ + 'prefixCls', + 'className', + 'children', + 'visibilityHeight', + ]); + + return ( + + { + this.state.visible ? +
+ {children || defaultElement} +
+ : null + } +
+ ); + } +} diff --git a/components/back-top/index.zh-CN.md b/components/back-top/index.zh-CN.md new file mode 100644 index 0000000000..441a4cc293 --- /dev/null +++ b/components/back-top/index.zh-CN.md @@ -0,0 +1,24 @@ +--- +category: Components +type: Other +chinese: 回到顶部 +english: BackTop +--- + +返回页面顶部的操作按钮。 + +## 何时使用 + +- 当页面内容区域比较长时; +- 当用户需要频繁返回顶部查看相关内容时。 + +## API + +> 有默认样式,距离底部 `50px`,可覆盖。 + +> 自定义样式宽高不大于 40px * 40px。 + +| 参数 | 说明 | 类型 | 默认值 | +|-------------|----------------|--------------------|--------------| +| visibilityHeight | 滚动高度达到此参数值才出现 `BackTop` | Number | 400 | +| onClick | 点击按钮的回调函数 | Function | - | diff --git a/components/back-top/style/index.js b/components/back-top/style/index.js new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/back-top/style/index.js @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/back-top/style/index.less b/components/back-top/style/index.less new file mode 100644 index 0000000000..c96e94552d --- /dev/null +++ b/components/back-top/style/index.less @@ -0,0 +1,33 @@ +@import "../../style/themes/default"; + +@backtop-prefix-cls: ant-back-top; + +.@{backtop-prefix-cls} { + z-index: @zindex-back-top; + position: fixed; + right: 100px; + bottom: 50px; + height: 40px; + width: 40px; + cursor: pointer; + + &-content { + height: 40px; + width: 40px; + border-radius: 20px; + background-color: rgba(64, 64, 64, 0.4); + color: #fff; + text-align: center; + transition: all .3s @ease-in-out; + + &:hover { + background-color: rgba(64, 64, 64, 0.6); + transition: all .3s @ease-in-out; + } + } + + &-icon { + font-size: 20px; + margin-top: 10px; + } +} diff --git a/components/badge/ScrollNumber.jsx b/components/badge/ScrollNumber.tsx similarity index 59% rename from components/badge/ScrollNumber.jsx rename to components/badge/ScrollNumber.tsx index 9b91e788a3..76ed566eea 100644 --- a/components/badge/ScrollNumber.jsx +++ b/components/badge/ScrollNumber.tsx @@ -1,21 +1,54 @@ -import React, { createElement } from 'react'; +import * as React from 'react'; +import { createElement, Component } from 'react'; +import {findDOMNode} from 'react-dom'; +import isCssAnimationSupported from '../_util/isCssAnimationSupported'; import assign from 'object-assign'; -import { isCssAnimationSupported } from 'css-animation'; +import omit from 'object.omit'; function getNumberArray(num) { return num ? - num.toString().split('').reverse().map(i => Number(i)) : []; + num.toString() + .split('') + .reverse() + .map(i => Number(i)) : []; } -class AntScrollNumber extends React.Component { +export default class ScrollNumber extends Component { + static defaultProps = { + prefixCls: 'ant-scroll-number', + count: null, + component: 'sup', + onAnimated() { + }, + height: 18, + }; + + static propTypes = { + count: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.number, + ]), + component: React.PropTypes.string, + onAnimated: React.PropTypes.func, + height: React.PropTypes.number, + }; + + lastCount: any; + constructor(props) { super(props); this.state = { animateStarted: true, - count: props.count + count: props.count, }; } + componentDidMount() { + if (!isCssAnimationSupported()) { + findDOMNode(this).className += ' not-support-css-animation'; + } + } + getPositionByNum(num, i) { if (this.state.animateStarted) { return 10 + num; @@ -59,10 +92,11 @@ class AntScrollNumber extends React.Component { } } - renderNumberList() { + renderNumberList(position) { const childrenToReturn = []; for (let i = 0; i < 30; i++) { - childrenToReturn.push(

{i % 10}

); + const currentClassName = (position === i) ? 'current' : null; + childrenToReturn.push(

{i % 10}

); } return childrenToReturn; } @@ -76,11 +110,12 @@ class AntScrollNumber extends React.Component { className: `${this.props.prefixCls}-only`, style: { transition: removeTransition && 'none', - transform: `translate3d(0, ${-position * height}px, 0)`, + WebkitTransform: `translateY(${-position * height}px)`, + transform: `translateY(${-position * height}px)`, height, }, key: i, - }, this.renderNumberList()); + }, this.renderNumberList(position)); } renderNumberElement() { @@ -93,41 +128,19 @@ class AntScrollNumber extends React.Component { } render() { - const props = assign({}, this.props, { - className: `${this.props.prefixCls} ${this.props.className}` + // fix https://fb.me/react-unknown-prop + const props = assign({}, omit(this.props, [ + 'count', + 'onAnimated', + 'component', + 'prefixCls', + ]), { + className: `${this.props.prefixCls} ${this.props.className}`, }); - const isBrowser = (typeof document !== 'undefined' && typeof window !== 'undefined'); - if (isBrowser && isCssAnimationSupported) { - return createElement( - this.props.component, - props, - this.renderNumberElement() - ); - } return createElement( this.props.component, props, - props.count + this.renderNumberElement() ); } } - -AntScrollNumber.defaultProps = { - prefixCls: 'ant-scroll-number', - count: null, - component: 'sup', - onAnimated() {}, - height: 18, -}; - -AntScrollNumber.propTypes = { - count: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.number - ]), - component: React.PropTypes.string, - onAnimated: React.PropTypes.func, - height: React.PropTypes.number, -}; - -export default AntScrollNumber; diff --git a/components/badge/demo/99plus.md b/components/badge/demo/99plus.md index 483ca77342..e3e12700bf 100644 --- a/components/badge/demo/99plus.md +++ b/components/badge/demo/99plus.md @@ -1,10 +1,17 @@ -# 大数字 +--- +order: 1 +title: + zh-CN: 大数字 + en-US: Overflowed count +--- -- order: 1 +## zh-CN 超过 99 的会显示为 `99+`。 ---- +## en-US + +`99+` is displayed when count is larger than `99`. ````jsx import { Badge } from 'antd'; diff --git a/components/badge/demo/basic.md b/components/badge/demo/basic.md index 4e4de952d5..93c123fe87 100644 --- a/components/badge/demo/basic.md +++ b/components/badge/demo/basic.md @@ -1,10 +1,17 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- -- order: 0 +## zh-CN 简单的徽章展示。 ---- +## en-US + +Simplest Usage. ````jsx import { Badge } from 'antd'; diff --git a/components/badge/demo/change.md b/components/badge/demo/change.md index 84b493f67c..2919fcfba6 100644 --- a/components/badge/demo/change.md +++ b/components/badge/demo/change.md @@ -1,10 +1,17 @@ -# 动态 +--- +order: 4 +title: + zh-CN: 动态 + en-US: Dynamic +--- -- order: 4 +## zh-CN 展示动态变化的效果。 ---- +## en-US + +The count will be animated as it changes. ````jsx import { Badge, Button, Icon } from 'antd'; @@ -57,7 +64,7 @@ const Test = React.createClass({
); - } + }, }); ReactDOM.render( diff --git a/components/badge/demo/dot.md b/components/badge/demo/dot.md index 797408fa31..61e71b3661 100644 --- a/components/badge/demo/dot.md +++ b/components/badge/demo/dot.md @@ -1,10 +1,17 @@ -# 讨嫌的小红点 +--- +order: 3 +title: + zh-CN: 讨嫌的小红点 + en-US: Red badge +--- -- order: 3 +## zh-CN 没有具体的数字。 ---- +## en-US + +This will simply display a red badge, without a specific count. ````jsx import { Badge, Icon } from 'antd'; diff --git a/components/badge/demo/link.md b/components/badge/demo/link.md index 45dadfcc88..9b2da4b871 100644 --- a/components/badge/demo/link.md +++ b/components/badge/demo/link.md @@ -1,10 +1,17 @@ -# 可点击 +--- +order: 2 +title: + zh-CN: 可点击 + en-US: Clickable +--- -- order: 2 +## zh-CN 用 a 标签进行包裹即可。 ---- +## en-US + +The badge can be wrapped with `a` tag to make it linkable. ````jsx import { Badge } from 'antd'; diff --git a/components/badge/demo/no-wrapper.md b/components/badge/demo/no-wrapper.md index d98102a275..d42c5aff45 100644 --- a/components/badge/demo/no-wrapper.md +++ b/components/badge/demo/no-wrapper.md @@ -1,12 +1,19 @@ -# 独立使用 +--- +order: 0 +title: + zh-CN: 独立使用 + en-US: Standalone +--- -- order: 0 +## zh-CN 不包裹任何元素即是独立使用,可自定样式展现。 > 在右上角的 badge 则限定为红色。 ---- +## en-US + +Used in standalone when children is empty. ````jsx import { Badge } from 'antd'; diff --git a/components/badge/demo/overflow.md b/components/badge/demo/overflow.md index 44e7d936ab..245f64c2a4 100644 --- a/components/badge/demo/overflow.md +++ b/components/badge/demo/overflow.md @@ -1,10 +1,17 @@ -# 封顶数字 +--- +order: 6 +title: + zh-CN: 封顶数字 + en-US: Customized overflow count +--- -- order: 6 +## zh-CN 超过 `overflowCount` 的会显示为 `${overflowCount}+`。 ---- +## en-US + +`${overflowCount}+` is displayed when count is larger than `overflowCount`. ````jsx import { Badge } from 'antd'; diff --git a/components/badge/index.en-US.md b/components/badge/index.en-US.md new file mode 100644 index 0000000000..9b40049c6a --- /dev/null +++ b/components/badge/index.en-US.md @@ -0,0 +1,30 @@ +--- +category: Components +type: Views +english: Badge +--- + +Small numerical value or status descriptors for UI elements. + +## When to use + +Badge normally appears in proximity to notification or head picture with eye-catching appeal, typically displaying unread messages count. + +## API + +```jsx + + + +``` + + +```jsx + +``` + +| Property | Description | Type | Default | +|----------------|-------------------------|------------|---------| +| count | Number to show in badge | Number | | +| overflowCount | Max count to show | Number | 99 | +| dot | whether to show red dot without number | Boolean | false | diff --git a/components/badge/index.jsx b/components/badge/index.jsx deleted file mode 100644 index 7076faaad3..0000000000 --- a/components/badge/index.jsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; -import Animate from 'rc-animate'; -import ScrollNumber from './ScrollNumber'; -import classNames from 'classnames'; - -class AntBadge extends React.Component { - render() { - let { count, prefixCls, overflowCount, className, style, children } = this.props; - const dot = this.props.dot; - - count = count > overflowCount ? `${overflowCount}+` : count; - - // dot mode don't need count - if (dot) { - count = ''; - } - - // null undefined "" "0" 0 - const hidden = (!count || count === '0') && !dot; - const scrollNumberCls = prefixCls + (dot ? '-dot' : '-count'); - const badgeCls = classNames({ - [className]: !!className, - [prefixCls]: true, - [`${prefixCls}-not-a-wrapper`]: !children, - }); - - return ( - - {children} - - { - hidden ? null : - - } - - - ); - } -} - -AntBadge.defaultProps = { - prefixCls: 'ant-badge', - count: null, - dot: false, - overflowCount: 99, -}; - -AntBadge.propTypes = { - count: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.number - ]), - dot: React.PropTypes.bool, - overflowCount: React.PropTypes.number, -}; - -export default AntBadge; diff --git a/components/badge/index.tsx b/components/badge/index.tsx new file mode 100644 index 0000000000..e326905133 --- /dev/null +++ b/components/badge/index.tsx @@ -0,0 +1,76 @@ +import * as React from 'react'; +import Animate from 'rc-animate'; +import ScrollNumber from './ScrollNumber'; +import classNames from 'classnames'; + +interface BadgeProps { + /** Number to show in badge */ + count: number | string; + /** Max count to show */ + overflowCount?: number; + /** whether to show red dot without number */ + dot?: boolean; + style?: React.CSSProperties; + prefixCls?: string; + className?: string; +} + +export default class Badge extends React.Component { + static defaultProps = { + prefixCls: 'ant-badge', + count: null, + dot: false, + overflowCount: 99, + }; + + static propTypes = { + count: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.number, + ]), + dot: React.PropTypes.bool, + overflowCount: React.PropTypes.number, + }; + + render() { + let { count, prefixCls, overflowCount, className, style, children, dot } = this.props; + + count = count > overflowCount ? `${overflowCount}+` : count; + + // dot mode don't need count + if (dot) { + count = ''; + } + + // null undefined "" "0" 0 + const hidden = (!count || count === '0') && !dot; + const scrollNumberCls = prefixCls + (dot ? '-dot' : '-count'); + const badgeCls = classNames({ + [className]: !!className, + [prefixCls]: true, + [`${prefixCls}-not-a-wrapper`]: !children, + }); + + return ( + + {children} + + { + hidden ? null : + + } + + + ); + } +} diff --git a/components/badge/index.md b/components/badge/index.zh-CN.md similarity index 92% rename from components/badge/index.md rename to components/badge/index.zh-CN.md index 0c7008bc9b..4a93a7e40e 100644 --- a/components/badge/index.md +++ b/components/badge/index.zh-CN.md @@ -1,9 +1,8 @@ -# Badge - -- category: Components -- chinese: 徽标数 -- type: 展示 - +--- +category: Components +chinese: 徽标数 +type: Views +english: Badge --- 图标右上角的圆形徽标数字。 diff --git a/style/components/badge.less b/components/badge/style/index.less similarity index 86% rename from style/components/badge.less rename to components/badge/style/index.less index 94b1a1726a..84f4f114f4 100644 --- a/style/components/badge.less +++ b/components/badge/style/index.less @@ -1,10 +1,13 @@ -@badge-prefix-cls: ~"@{css-prefix}badge"; -@number-prefix-cls: ~"@{css-prefix}scroll-number"; +@import "../../style/themes/default"; + +@badge-prefix-cls: ant-badge; +@number-prefix-cls: ant-scroll-number; .@{badge-prefix-cls} { position: relative; display: inline-block; line-height: 1; + vertical-align: middle; &-count { position: absolute; @@ -78,6 +81,15 @@ a & { display: inline-block; transition: transform .3s @ease-in-out; } + // for IE8/9 display + &.not-support-css-animation &-only { + > p { + display: none; + &.current { + display: block; + } + } + } } @keyframes antZoomBadgeIn { diff --git a/components/badge/style/index.tsx b/components/badge/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/badge/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/breadcrumb/Breadcrumb.tsx b/components/breadcrumb/Breadcrumb.tsx new file mode 100644 index 0000000000..381660040b --- /dev/null +++ b/components/breadcrumb/Breadcrumb.tsx @@ -0,0 +1,85 @@ +import * as React from 'react'; +import { cloneElement } from 'react'; +import BreadcrumbItem from './BreadcrumbItem'; + +const defaultNameRender = (breadcrumbName, route, params) => { + if (!breadcrumbName) { + return null; + } + const paramsKeys = Object.keys(params).join('|'); + const name = breadcrumbName.replace( + new RegExp(`:(${paramsKeys})`, 'g'), + (replacement, key) => params[key] || replacement + ); + return {name}; +}; + +interface BreadcrumbProps { + prefixCls?: string; + routes?: Array; + params?: Object; + separator?: string | React.ReactNode; + linkRender?: (link, name, paths: Array) => React.ReactNode; + nameRender?: (breadcrumbName, route, params) => React.ReactNode; + style?: React.CSSProperties; +} + +export default class Breadcrumb extends React.Component { + static Item: any; + + static defaultProps = { + prefixCls: 'ant-breadcrumb', + separator: '/', + linkRender: (href, name) => {name}, + nameRender: defaultNameRender, + }; + + static propTypes = { + prefixCls: React.PropTypes.string, + separator: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.element, + ]), + routes: React.PropTypes.array, + params: React.PropTypes.object, + linkRender: React.PropTypes.func, + nameRender: React.PropTypes.func, + }; + + render() { + let crumbs; + const { separator, prefixCls, routes, params, children, linkRender, nameRender } = this.props; + if (routes && routes.length > 0) { + const paths = []; + const lastPath = routes.length - 1; + crumbs = routes.map((route, i) => { + route.path = route.path || ''; + let path = route.path.replace(/^\//, ''); + Object.keys(params).forEach(key => { + path = path.replace(`:${key}`, params[key]); + }); + if (path) { + paths.push(path); + } + const name = nameRender(route.breadcrumbName, route, params); + if (name) { + const link = (i === lastPath) ? name : linkRender(`/${paths.join('/')}`, name, paths); + return {link}; + } + return null; + }); + } else { + crumbs = React.Children.map(children, (element: any, index) => { + return cloneElement(element, { + separator, + key: index, + }); + }); + } + return ( +
+ {crumbs} +
+ ); + } +} diff --git a/components/breadcrumb/BreadcrumbItem.tsx b/components/breadcrumb/BreadcrumbItem.tsx new file mode 100644 index 0000000000..580d9d9d0e --- /dev/null +++ b/components/breadcrumb/BreadcrumbItem.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import splitObject from '../_util/splitObject'; + +interface BreadcrumbItemProps { + separator?: React.ReactNode; + href?: string; +} + +export default class BreadcrumbItem extends React.Component { + static defaultProps = { + prefixCls: 'ant-breadcrumb', + separator: '/', + }; + + static propTypes = { + prefixCls: React.PropTypes.string, + separator: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.element, + ]), + href: React.PropTypes.string, + }; + + render() { + const [{ prefixCls, separator, children }, restProps] = splitObject( + this.props, ['prefixCls', 'separator', 'children'] + ); + let link; + if ('href' in this.props) { + link = {children}; + } else { + link = {children}; + } + return ( + + {link} + {separator} + + ); + } +} diff --git a/components/breadcrumb/demo/basic.md b/components/breadcrumb/demo/basic.md index 929a62de21..1d5f8bb08e 100644 --- a/components/breadcrumb/demo/basic.md +++ b/components/breadcrumb/demo/basic.md @@ -1,20 +1,27 @@ -# 基本 - -- order: 0 - -最简单的用法,存在 `href` 表示可点。 - --- +order: 0 +title: + zh-CN: 基本 + en-US: Basic Usage +--- + +## zh-CN + +最简单的用法。 + +## en-US + +The simplest use ````jsx import { Breadcrumb } from 'antd'; ReactDOM.render( - 首页 - 应用中心 - 应用列表 - 某应用 + Home + Application Center + Application List + An Application , mountNode); ```` diff --git a/components/breadcrumb/demo/router.md b/components/breadcrumb/demo/router.md index cd8c129055..35ff00be58 100644 --- a/components/breadcrumb/demo/router.md +++ b/components/breadcrumb/demo/router.md @@ -1,60 +1,61 @@ -# 路由 +--- +order: 2 +iframe: true +title: + zh-CN: 路由 + en-US: React Router Integration +--- -- order: 2 +## zh-CN 和 `react-router@2.x` 进行结合使用。 ---- +## en-US + +Used together with `react-router@2.x`. ````jsx -const ReactRouter = require('react-router'); -let { Router, Route, Link, hashHistory } = ReactRouter; +import { Router, Route, Link, hashHistory } from 'react-router'; import { Breadcrumb } from 'antd'; -const Apps = React.createClass({ - render() { - return ( -
    -
  • - 应用1:详情 -
  • -
  • - 应用2:详情 -
  • -
- ); - } -}); +const Apps = () => ( +
    +
  • + Application1:Detail +
  • +
  • + Application2:Detail +
  • +
+); -const Home = React.createClass({ - render() { - return ( -
-
- 首页 - 应用列表 -
- {this.props.children || 'Home'} -
- 点击上面的导航切换页面,面包屑在下面: -
- -
- ); - } -}); +const Home = (props) => ( +
+
+ Home + Application List +
+ {props.children || 'Home'} +
+ Click the navigation above to switch: +
+ +
+); ReactDOM.render( - - - - + + + + @@ -62,7 +63,10 @@ ReactDOM.render( , mountNode); ```` - +```` diff --git a/components/breadcrumb/demo/separator.md b/components/breadcrumb/demo/separator.md index bbbc697900..79b39ae20b 100644 --- a/components/breadcrumb/demo/separator.md +++ b/components/breadcrumb/demo/separator.md @@ -1,20 +1,27 @@ -# 分隔符 +--- +order: 3 +title: + zh-CN: 分隔符 + en-US: Configuring the Separator +--- -- order: 3 +## zh-CN 使用 `separator=">"` 可以自定义分隔符。 ---- +## en-US + +The separator can be customized by setting the separator property: separator=">" ````jsx import { Breadcrumb } from 'antd'; ReactDOM.render( - 首页 - 应用中心 - 应用列表 - 某应用 + Home + Application Center + Application List + An Application , mountNode); ```` diff --git a/components/breadcrumb/demo/withIcon.md b/components/breadcrumb/demo/withIcon.md index 8740d242cd..559f2195c0 100644 --- a/components/breadcrumb/demo/withIcon.md +++ b/components/breadcrumb/demo/withIcon.md @@ -1,10 +1,17 @@ -# 带有图标的 +--- +order: 1 +title: + zh-CN: 带有图标的 + en-US: With an Icon +--- -- order: 1 +## zh-CN 图标放在文字前面。 ---- +## en-US + +The icon should be placed in front of the text. ````jsx import { Breadcrumb, Icon } from 'antd'; @@ -16,12 +23,11 @@ ReactDOM.render( - 应用列表 + Application List - 应用 + Application , mountNode); ```` - diff --git a/components/breadcrumb/index.en-US.md b/components/breadcrumb/index.en-US.md new file mode 100644 index 0000000000..c376881b46 --- /dev/null +++ b/components/breadcrumb/index.en-US.md @@ -0,0 +1,32 @@ +--- +category: Components +type: Navigation +title: Breadcrumb +--- + +A breadcrumb displays the current location within a hierarchy. It allows going back to states higher up within the hierarchy. + +## When to use + +- When the system has more than two layers in a hierarchy. +- When you need to inform the user of where they are. +- When the user may need to navigate back to a higher level When the application has multi-layer architecture. + +## API + +```html + + Home + Application Center + Application List + An Application + +``` + +| Property | Description | Type | Optional | Default | +|-----------|-----------------------------------|-----------------|---------|--------| +| routes | The routing stack information of router | Array | | - | +| params | Routing parameter | Object | | - | +| separator | Custom separator | String or Element | | '/' | +| linkRender | Custom link function,and react-router configuration | Function(href, name, paths) | | - | +| nameRender | Custom link function,and react-router configuration | Function(breadcrumbName, route, params) | | - | diff --git a/components/breadcrumb/index.jsx b/components/breadcrumb/index.jsx deleted file mode 100644 index f9e0f8bd23..0000000000 --- a/components/breadcrumb/index.jsx +++ /dev/null @@ -1,95 +0,0 @@ -import React, { cloneElement } from 'react'; - -const BreadcrumbItem = React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-breadcrumb', - separator: '/', - }; - }, - propTypes: { - prefixCls: React.PropTypes.string, - separator: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.element, - ]), - href: React.PropTypes.string, - }, - render() { - const { prefixCls, separator, children } = this.props; - let link = {children}; - if (typeof this.props.href === 'undefined') { - link = {children}; - } - return ( - - {link} - {separator} - - ); - } -}); - -const Breadcrumb = React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-breadcrumb', - separator: '/', - }; - }, - propTypes: { - prefixCls: React.PropTypes.string, - separator: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.element, - ]), - routes: React.PropTypes.array, - params: React.PropTypes.object, - }, - render() { - let crumbs; - const { separator, prefixCls, routes, params, children } = this.props; - if (routes && routes.length > 0) { - const paths = []; - crumbs = routes.map((route, i) => { - if (!route.breadcrumbName) { - return null; - } - const name = route.breadcrumbName.replace(/\:(.*)/g, (replacement, key) => { - return params[key] || replacement; - }); - - let link; - let path = route.path.replace(/^\//, ''); - Object.keys(params).forEach(key => { - path = path.replace(`:${key}`, params[key]); - }); - if (path) { - paths.push(path); - } - - if (i === routes.length - 1) { - link = {name}; - } else { - link = {name}; - } - return {link}; - }); - } else { - crumbs = React.Children.map(children, (element, index) => { - return cloneElement(element, { - separator, - key: index, - }); - }); - } - return ( -
- {crumbs} -
- ); - } -}); - -Breadcrumb.Item = BreadcrumbItem; -export default Breadcrumb; diff --git a/components/breadcrumb/index.tsx b/components/breadcrumb/index.tsx new file mode 100644 index 0000000000..b77d03b267 --- /dev/null +++ b/components/breadcrumb/index.tsx @@ -0,0 +1,5 @@ +import Breadcrumb from './Breadcrumb'; +import BreadcrumbItem from './BreadcrumbItem'; + +Breadcrumb.Item = BreadcrumbItem; +export default Breadcrumb; diff --git a/components/breadcrumb/index.md b/components/breadcrumb/index.zh-CN.md similarity index 57% rename from components/breadcrumb/index.md rename to components/breadcrumb/index.zh-CN.md index 34685f895b..15aaaf793c 100644 --- a/components/breadcrumb/index.md +++ b/components/breadcrumb/index.zh-CN.md @@ -1,9 +1,8 @@ -# Breadcrumb - -- category: Components -- chinese: 面包屑 -- type: 导航 - +--- +category: Components +chinese: 面包屑 +type: Navigation +english: Breadcrumb --- 显示当前页面在系统层级结构中的位置,并能向上返回。 @@ -11,7 +10,7 @@ ## 何时使用 - 当系统拥有超过两级以上的层级结构时; -- 当需要告知用户“你在哪里”时; +- 当需要告知用户『你在哪里』时; - 当需要向上导航的功能时。 ## API @@ -19,22 +18,16 @@ ```html 首页 - 应用中心 - 应用列表 + 应用中心 + 应用列表 某应用 ``` -### Breadcrumb - | 参数 | 说明 | 类型 | 可选值 | 默认值 | -|-----------|-----------------------------------|-------------------|---------|--------| +|-----------|-----------------------------------|-----------------|---------|--------| | routes | router 的路由栈信息 | Array | | - | | params | 路由的参数 | Object | | - | | separator | 分隔符自定义 | String or Element | | '/' | - -### Breadcrumb.Item - -| 参数 | 说明 | 类型 | 可选值 | 默认值 | -|-----------|------------------------------------------|------------|---------|--------| -| href | 链接,如不传则不可点击 | string | | - | +| linkRender | 自定义链接函数,和 react-router 配置使用 | Function(href, name, paths) | | - | +| nameRender | 自定义文字函数,和 react-router 配置使用 | Function(breadcrumbName, route, params) | | - | diff --git a/style/components/breadcrumb.less b/components/breadcrumb/style/index.less similarity index 51% rename from style/components/breadcrumb.less rename to components/breadcrumb/style/index.less index fcf2c73065..dbfbeb8021 100644 --- a/style/components/breadcrumb.less +++ b/components/breadcrumb/style/index.less @@ -1,16 +1,22 @@ +@import "../../style/themes/default"; + @breadcrumb-prefix-cls: ant-breadcrumb; .@{breadcrumb-prefix-cls} { color: #999; - font-size: 12px; + font-size: @font-size-base; a { - color: #666; + color: @text-color; + transition: color .3s; + &:hover { + color: tint(@primary-color, 20%); + } } & > span:last-child { font-weight: bold; - color: #666; + color: @text-color; } & > span:last-child &-separator { @@ -22,7 +28,9 @@ color: @border-color-base; } - .anticon + span { - margin-left: 4px; + &-link { + > .anticon + span { + margin-left: 4px; + } } } diff --git a/components/breadcrumb/style/index.tsx b/components/breadcrumb/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/breadcrumb/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/button/button-group.jsx b/components/button/button-group.jsx deleted file mode 100644 index 46829114f3..0000000000 --- a/components/button/button-group.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; - -const prefix = 'ant-btn-group-'; - -export default class ButtonGroup extends React.Component { - render() { - const { size, className, ...others } = this.props; - - // large => lg - // small => sm - const sizeCls = ({ - large: 'lg', - small: 'sm', - })[size] || ''; - - const classes = classNames({ - 'ant-btn-group': true, - [prefix + sizeCls]: sizeCls, - [className]: className - }); - - return
; - } -} -ButtonGroup.propTypes = { - size: React.PropTypes.string, -}; diff --git a/components/button/button-group.tsx b/components/button/button-group.tsx new file mode 100644 index 0000000000..8bb5b9d71f --- /dev/null +++ b/components/button/button-group.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import classNames from 'classnames'; +import splitObject from '../_util/splitObject'; + +const prefix = 'ant-btn-group-'; + +type ButtonSize = 'small' | 'large' + +interface ButtonGroupProps { + size?: ButtonSize; + style?: React.CSSProperties; + className?: string; +} + +export default function ButtonGroup(props: ButtonGroupProps) { + const [{ size, className }, others] = splitObject(props, ['size', 'className']); + + // large => lg + // small => sm + const sizeCls = ({ + large: 'lg', + small: 'sm', + })[size] || ''; + + const classes = classNames({ + 'ant-btn-group': true, + [prefix + sizeCls]: sizeCls, + [className]: className, + }); + + return
; +} diff --git a/components/button/button.jsx b/components/button/button.jsx deleted file mode 100644 index f139d96059..0000000000 --- a/components/button/button.jsx +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; -import { findDOMNode } from 'react-dom'; - -const rxTwoCNChar = /^[\u4e00-\u9fa5]{2,2}$/; -const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar); -function isString(str) { - return typeof str === 'string'; -} - -const prefix = 'ant-btn-'; - -// Insert one space between two chinese characters automatically. -function insertSpace(child) { - if (isString(child) && isTwoCNChar(child)) { - return child.split('').join(' '); - } - - if (isString(child.type) && isTwoCNChar(child.props.children)) { - return React.cloneElement(child, {}, - child.props.children.split('').join(' ')); - } - - return child; -} - -function clearButton(button) { - button.className = button.className.replace(`${prefix}clicked`, ''); -} - -export default class Button extends React.Component { - handleClick(...args) { - // Add click effect - const buttonNode = findDOMNode(this); - clearButton(buttonNode); - setTimeout(() => buttonNode.className += ` ${prefix}clicked`, 10); - clearTimeout(this.timeout); - this.timeout = setTimeout(() => clearButton(buttonNode), 500); - - this.props.onClick(...args); - } - render() { - const props = this.props; - const { type, shape, size, className, htmlType, children, ...others } = props; - - // large => lg - // small => sm - const sizeCls = ({ - large: 'lg', - small: 'sm', - })[size] || ''; - - const classes = classNames({ - 'ant-btn': true, - [prefix + type]: type, - [prefix + shape]: shape, - [prefix + sizeCls]: sizeCls, - [`${prefix}loading`]: ('loading' in props && props.loading !== false), - [className]: className, - }); - - const kids = React.Children.map(children, insertSpace); - - return ( - - ); - } -} - -Button.propTypes = { - type: React.PropTypes.string, - shape: React.PropTypes.string, - size: React.PropTypes.string, - htmlType: React.PropTypes.string, - onClick: React.PropTypes.func, - loading: React.PropTypes.bool, - className: React.PropTypes.string, -}; - -Button.defaultProps = { - onClick() {}, -}; diff --git a/components/button/button.tsx b/components/button/button.tsx new file mode 100644 index 0000000000..10a7a189a9 --- /dev/null +++ b/components/button/button.tsx @@ -0,0 +1,136 @@ +import * as React from 'react'; +import classNames from 'classnames'; +import { findDOMNode } from 'react-dom'; +import Icon from '../icon'; +import splitObject from '../_util/splitObject'; +const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/; +const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar); +function isString(str) { + return typeof str === 'string'; +} + +// Insert one space between two chinese characters automatically. +function insertSpace(child) { + if (isString(child.type) && isTwoCNChar(child.props.children)) { + return React.cloneElement(child, {}, + child.props.children.split('').join(' ')); + } + if (isString(child)) { + if (isTwoCNChar(child)) { + child = child.split('').join(' '); + } + return {child}; + } + return child; +} + +type ButtonType = 'primary' | 'ghost' | 'dashed' +type ButtonShape = 'circle' | 'circle-outline' +type ButtonSize = 'small' | 'large' + +interface ButtonProps { + type?: ButtonType; + htmlType?: string; + icon?: string; + shape?: ButtonShape; + size?: ButtonSize; + onClick?: React.FormEventHandler; + loading?: boolean; + disabled?: boolean; + style?: React.CSSProperties; + prefixCls?: string; +} + +export default class Button extends React.Component { + static Group: any; + + static defaultProps = { + prefixCls: 'ant-btn', + onClick() {}, + loading: false, + }; + + static propTypes = { + type: React.PropTypes.string, + shape: React.PropTypes.oneOf(['circle', 'circle-outline']), + size: React.PropTypes.oneOf(['large', 'default', 'small']), + htmlType: React.PropTypes.oneOf(['submit', 'button', 'reset']), + onClick: React.PropTypes.func, + loading: React.PropTypes.bool, + className: React.PropTypes.string, + icon: React.PropTypes.string, + }; + + timeout: any; + clickedTimeout: any; + + componentWillUnmount() { + if (this.clickedTimeout) { + clearTimeout(this.clickedTimeout); + } + if (this.timeout) { + clearTimeout(this.timeout); + } + } + + clearButton = (button) => { + button.className = button.className.replace(` ${this.props.prefixCls}-clicked`, ''); + } + + handleClick = (e) => { + // Add click effect + const buttonNode = findDOMNode(this); + this.clearButton(buttonNode); + this.clickedTimeout = setTimeout(() => buttonNode.className += ` ${this.props.prefixCls}-clicked`, 10); + clearTimeout(this.timeout); + this.timeout = setTimeout(() => this.clearButton(buttonNode), 500); + + this.props.onClick(e); + } + + // Handle auto focus when click button in Chrome + handleMouseUp = (e) => { + (findDOMNode(this) as HTMLElement).blur(); + if (this.props.onMouseUp) { + this.props.onMouseUp(e); + } + } + + render() { + const props = this.props; + const [{ type, shape, size, className, htmlType, children, icon, loading, prefixCls }, others] = splitObject(props, + ['type', 'shape', 'size', 'className', 'htmlType', 'children', 'icon', 'loading', 'prefixCls']); + + // large => lg + // small => sm + const sizeCls = ({ + large: 'lg', + small: 'sm', + })[size] || ''; + + const classes = classNames({ + [prefixCls]: true, + [`${prefixCls}-${type}`]: type, + [`${prefixCls}-${shape}`]: shape, + [`${prefixCls}-${sizeCls}`]: sizeCls, + [`${prefixCls}-icon-only`]: !children && icon, + [`${prefixCls}-loading`]: loading, + [className]: className, + }); + + const iconType = loading ? 'loading' : icon; + + const kids = React.Children.map(children, insertSpace); + + return ( + + ); + } +} diff --git a/components/button/demo/basic.md b/components/button/demo/basic.md index c4d69feb75..af283c35ae 100644 --- a/components/button/demo/basic.md +++ b/components/button/demo/basic.md @@ -1,30 +1,36 @@ -# 按钮类型 - -- order: 0 - -按钮有三种类型:主按钮、次按钮、幽灵按钮。 - -通过设置 `type` 为 `primary` `ghost` 可分别创建主按钮、幽灵按钮,若不设置 `type` 值则为次按钮。不同的样式可以用来区别其重要程度。 - -主按钮和次按钮可独立使用,需要强引导用主按钮。幽灵按钮用于和主按钮组合。 - --- +order: 0 +title: + zh-CN: 按钮类型 + en-US: Type +--- + +## zh-CN + +按钮有四种类型:主按钮、次按钮、幽灵按钮、虚线按钮。 + +通过设置 `type` 为 `primary` `ghost` `dashed` 可分别创建主按钮、幽灵按钮、虚线按钮,若不设置 `type` 值则为次按钮。不同的样式可以用来区别其重要程度。 + +主按钮和次按钮可独立使用,幽灵按钮用于和主按钮组合。需要强引导用主按钮,切记主按钮在同一个操作区域最多出现一次。 + +## en-US + +There are primary button, default button, ghost button and dashed button in antd. + +`type` can be set as `primary` or `ghost` or `dashed`, in order to create primary button or ghost button or dashed button. If nothing is provided to `type`, we will get default button. Users can tell the significance of button from it's appearance. + +Primary button and default button can be used without other button, but ghost button must be used with primary button. ````jsx import { Button } from 'antd'; -ReactDOM.render(
- - - - -
, -mountNode); +ReactDOM.render( +
+ + + + +
, + mountNode +); ```` - - diff --git a/components/button/demo/button-group.md b/components/button/demo/button-group.md index 4a6ce76c8a..5c146e4377 100644 --- a/components/button/demo/button-group.md +++ b/components/button/demo/button-group.md @@ -1,85 +1,75 @@ -# 按钮组合 +--- +order: 5 +title: + zh-CN: 按钮组合 + en-US: Button Group +--- -- order: 5 +## zh-CN 可以将多个 `Button` 放入 `Button.Group` 的容器中。 通过设置 `size` 为 `large` `small` 分别把按钮组合设为大、小尺寸。若不设置 `size`,则尺寸为中。 ---- +## en-US + +Buttons can be grouped by placing multiple `Button` components into a `Button.Group`. + +The `size` can be set to `large`, `small` or left unset resulting in a default size. ````jsx import { Button, Icon } from 'antd'; const ButtonGroup = Button.Group; -ReactDOM.render(
-

基本组合

- - - - - - - - - - - - - - - +ReactDOM.render( +
+

Basic

+ + + + + + + + + + + + + + + -

带图标按钮组合

- - - - - - - - +

With Icon

+ + + + + + - - - - - - -

尺寸

- - - - - - - - - - - - - - - -
-, mountNode); +

Size

+ + + + + + + + + + + + +
, + mountNode +); ```` diff --git a/components/button/demo/disabled.md b/components/button/demo/disabled.md index c082af428a..f91903e592 100644 --- a/components/button/demo/disabled.md +++ b/components/button/demo/disabled.md @@ -1,33 +1,35 @@ -# 不可用 +--- +order: 3 +title: + zh-CN: 不可用状态 + en-US: Disabled +--- -- order: 3 +## zh-CN 添加 `disabled` 属性即可让按钮处于不可用状态,同时按钮样式也会改变。 ---- +## en-US + +To mark a button as disabled, add the `disabled` property to the `Button`. ````jsx import { Button } from 'antd'; -ReactDOM.render(
- - -
- - -
- - -
- - -
-, mountNode); +ReactDOM.render( +
+ + +
+ + +
+ + +
+ + +
, + mountNode +); ```` - - diff --git a/components/button/demo/icon.md b/components/button/demo/icon.md index 877040a541..1c3c2322f3 100644 --- a/components/button/demo/icon.md +++ b/components/button/demo/icon.md @@ -1,57 +1,33 @@ -# 图标按钮 - -- order: 6 - -`Button` 内可以嵌套图标,图标可以放在文字前、后,也可以单独存在。 - +--- +order: 1 +title: + zh-CN: 图标按钮 + en-US: Icon --- +## zh-CN + +当需要在 `Button` 内嵌入 `Icon` 时,可以设置 `icon` 属性,或者直接在 `Button` 内使用 `Icon` 组件。 + +如果想控制 `Icon` 具体的位置,只能直接使用 `Icon` 组件,而非 `icon` 属性。 + +## en-US + +`Button` components can contain an `Icon`. This is done by setting the `icon` property or placing an `Icon` component within the `Button` + +If you want specific control over the positioning and placement of the `Icon`, then that should be done by placing the `Icon` component within the `Button` rather than using the `icon` property. + ````jsx -import { Button, Icon } from 'antd'; +import { Button } from 'antd'; -ReactDOM.render(
- - - - - - - - - -
- - - - -
, -mountNode); +ReactDOM.render( +
+ +
+ +
, + mountNode +); ```` - - diff --git a/components/button/demo/loading.md b/components/button/demo/loading.md index e0e7e7e0a9..1ecbb1cc96 100644 --- a/components/button/demo/loading.md +++ b/components/button/demo/loading.md @@ -1,13 +1,20 @@ -# 加载中 +--- +order: 4 +title: + zh-CN: 加载中状态 + en-US: Loading +--- -- order: 4 +## zh-CN 添加 `loading` 属性即可让按钮处于加载状态,最后两个按钮演示点击后进入加载状态。 ---- +## en-US + +A loading indicator can be added to a button by setting the `loading` property on the `Button`. ````jsx -import { Button, Icon } from 'antd'; +import { Button } from 'antd'; const App = React.createClass({ getInitialState() { @@ -25,33 +32,23 @@ const App = React.createClass({ render() { return (
-
-
); - } + }, }); ReactDOM.render(, mountNode); ```` - - diff --git a/components/button/demo/shape.md b/components/button/demo/shape.md deleted file mode 100644 index 1df33609b8..0000000000 --- a/components/button/demo/shape.md +++ /dev/null @@ -1,41 +0,0 @@ -# 按钮形状 - -- order: 1 - -通过设置 `shape` 为 `circle` `circle-outline`,可以把按钮形状设为圆形,并且 `circle-outline` 在 hover 时会有动画效果。 - ---- - -````jsx -import { Button, Icon } from 'antd'; - -ReactDOM.render(
- - - -
- - - -
-, mountNode); -```` - - diff --git a/components/button/demo/size.md b/components/button/demo/size.md index eb083c7419..087c7ffadf 100644 --- a/components/button/demo/size.md +++ b/components/button/demo/size.md @@ -1,26 +1,28 @@ -# 按钮尺寸 +--- +order: 2 +title: + zh-CN: 按钮尺寸 + en-US: Size +--- -- order: 2 +## zh-CN 按钮有大、中、小三种尺寸。 通过设置 `size` 为 `large` `small` 分别把按钮设为大、小尺寸。若不设置 `size`,则尺寸为中。 ---- +## en-US + +Ant Design supports a default button size as well as a large and small size. + +If a large or small button is desired, set the `size` property to either `large` or `small` respectively. Omit the `size` property for a button with the default size. ````jsx import { Button } from 'antd'; ReactDOM.render(
- - - -
-, mountNode); + + + +
, mountNode); ```` - - diff --git a/components/button/index.en-US.md b/components/button/index.en-US.md new file mode 100644 index 0000000000..993135178b --- /dev/null +++ b/components/button/index.en-US.md @@ -0,0 +1,34 @@ +--- +category: Components +type: Basic +title: Button +--- + +To trigger an operation. + +## When To Use + +A button means an operation(or a series of operations). Click a button will trigger corresponding business logic. + +## API + +To get a customized button, just set `type`/`shape`/`size`/`loading`/`disabled`. + +Property | Description | Type | Default +-----|-----|-----|------ +type | can be set to `primary` `ghost` or omitted | string | - +htmlType | to set the original `type` of `button`, see: [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-type) | string | `button` +icon | set the icon of button, see: Icon component | string | - +shape | can be set to `circle` `circle-outline` or omitted | string | - +size | can be set to `small` `large` or omitted | string | `default` +loading | to set the loading status of button | boolean | false +onClick | set the handler to handle `click` event | function | - + +`` will be rendered into ``, and all the properties which are not listed above will be transferred to the `` 最终会被渲染为 ``,并且除了上表中的属性,其它属性都会直接传到 `` diff --git a/components/button/index.jsx b/components/button/index.tsx similarity index 100% rename from components/button/index.jsx rename to components/button/index.tsx diff --git a/components/button/index.zh-CN.md b/components/button/index.zh-CN.md new file mode 100644 index 0000000000..6c34fa30af --- /dev/null +++ b/components/button/index.zh-CN.md @@ -0,0 +1,37 @@ +--- +category: Components +type: Basic +title: Button +subtitle: 按钮 +--- + +按钮用于开始一个即时操作。 + +## 何时使用 + +标记了一个(或封装一组)操作命令,响应用户点击行为,触发相应的业务逻辑。 + +## API + +通过设置 Button 的属性来产生不同的按钮样式,推荐顺序为:`type` -> `shape` -> `size` -> `loading` -> `disabled` + +按钮的属性说明如下: + +属性 | 说明 | 类型 | 默认值 +-----|-----|-----|------ +type | 设置按钮类型,可选值为 `primary` `ghost` 或者不设 | string | - +htmlType | 设置 `button` 原生的 `type` 值,可选值请参考 [HTML 标准](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-type) | string | `button` +icon | 设置按钮的图标类型 | string | - +shape | 设置按钮形状,可选值为 `circle` `circle-outline` 或者不设 | string | - +size | 设置按钮大小,可选值为 `small` `large` 或者不设 | string | `default` +loading | 设置按钮载入状态 | boolean | false +onClick | `click` 事件的 handler | function | - + +`` 最终会被渲染为 ``,并且除了上表中的属性,其它属性都会直接传到 ``。 + + diff --git a/style/components/button.less b/components/button/style/index.less similarity index 59% rename from style/components/button.less rename to components/button/style/index.less index 00a96720a1..ee3ed65a67 100644 --- a/style/components/button.less +++ b/components/button/style/index.less @@ -1,5 +1,8 @@ -@import "../mixins/index"; -@btn-prefix-cls: ~"@{css-prefix}btn"; +@import "../../style/themes/default"; +@import "../../style/mixins/index"; +@import "./mixin"; + +@btn-prefix-cls: ant-btn; // Button styles // ----------------------------- @@ -46,45 +49,39 @@ .btn-circle(@btn-prefix-cls); } - &-circle-outline { - .btn-circle-outline; - } - - &:after { - font-family: anticon; - content: "\e6a1"; + &:before { position: absolute; - opacity: 0; - visibility: hidden; - transition: opacity 0.5s ease; + top: -1px; + left: -1px; + bottom: -1px; + right: -1px; + background: #fff; + opacity: 0.35; + content: ''; + border-radius: inherit; + z-index: 1; + transition: opacity .2s; + pointer-events: none; + display: none; } &&-loading { padding-left: 29px; pointer-events: none; - opacity: 0.75; - &:after { - .animation(loadingCircle 1s infinite linear); - height: 12px; - line-height: 12px; - left: 10px; - top: 50%; - margin-top: -6px; - opacity: 1; - visibility: visible; + position: relative; + .anticon { + margin-left: -14px; + transition: all .3s @ease-in-out; } - > .anticon { - display: none; - & + span { - margin-left: 0; - } + &:before { + display: block; } } &-sm&-loading { padding-left: 24px; - &:after { - left: 7px; + .anticon { + margin-left: -17px; } } @@ -92,31 +89,39 @@ .btn-group(@btn-prefix-cls); } + &:not(&-circle):not(&-circle-outline)&-icon-only { + padding-left: 8px; + padding-right: 8px; + } + // To ensure that a space will be placed between character and `Icon`. > .@{iconfont-css-prefix} + span, > span + .@{iconfont-css-prefix} { margin-left: 0.5em; } - &-clicked:before { + &-clicked:after { content: ''; position: absolute; - top: -6px; - left: -6px; - bottom: -6px; - right: -6px; + top: -1px; + left: -1px; + bottom: -1px; + right: -1px; border-radius: inherit; - z-index: -1; - background: inherit; - opacity: 1; - transform: scale3d(0.5, 0.5, 1); - animation: buttonEffect 0.48s ease forwards; + border: 0 solid @primary-color; + opacity: 0.4; + animation: buttonEffect 0.32s ease forwards; + display: block; } } @keyframes buttonEffect { to { opacity: 0; - transform: scale3d(1, 1, 1); + top: -5px; + left: -5px; + bottom: -5px; + right: -5px; + border-width: 5px; } } diff --git a/components/button/style/index.tsx b/components/button/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/button/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/style/mixins/button.less b/components/button/style/mixin.less similarity index 79% rename from style/mixins/button.less rename to components/button/style/mixin.less index d1987ed60b..ee4d4b48a8 100644 --- a/style/mixins/button.less +++ b/components/button/style/mixin.less @@ -35,6 +35,20 @@ color: @color; background-color: @background; border-color: @border; + // a inside Button which only work in Chrome + // http://stackoverflow.com/a/17253457 + > a:only-child { + color: currentColor; + &:after { + content: ''; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + background: transparent; + } + } } .button-group-base(@btnClassName) { @@ -81,12 +95,9 @@ white-space: nowrap; line-height: @line-height-base; .button-size(@btn-padding-base; @font-size-base; @border-radius-base); - .user-select(none); - .transition(all .3s @ease-in-out); + user-select: none; + transition: all .3s @ease-in-out; transform: translate3d(0, 0, 0); - // Fix for ie8 border-radius - // http://css3pie.com/documentation/known-issues/#z-index - position: relative\0; > .@{iconfont-css-prefix} { line-height: 1; @@ -104,12 +115,15 @@ &:not([disabled]):active { outline: 0; - .transition(none); + transition: none; } &.disabled, &[disabled] { cursor: @cursor-disabled; + > * { + pointer-events: none; + } } &-lg { @@ -192,50 +206,6 @@ } } -// circle button with stroke border -.btn-circle-outline() { - position: relative; - - &:not([disabled]):before { - position: absolute; - top: 0; - left: 0; - display: inline-block; - .opacity(0); - width: 100%; - height: 100%; - border-radius: 50% 50%; - content: " "; - .scale(0, 0); - .transition(all .3s @ease-in-out); - z-index: 0; - background-color: @primary-color; - } - - &:not([disabled]):hover, - &:not([disabled]):focus, - &:not([disabled]):active, - &:not([disabled]).active { - > .@{iconfont-css-prefix} { - color: @btn-primary-color; - position: relative; - } - } - - &:not([disabled]):hover:before, - &:not([disabled]):focus:before, - &:not([disabled]):active:before, - &:not([disabled]).active:before { - .opacity(1); - .scale(1, 1); - } - - &:not([disabled]):active:before, - &:not([disabled]).active:before { - background-color: tint(@primary-color, 20%); - } -} - // Horizontal button groups styl // -------------------------------------------------- .btn-group(@btnClassName: btn) { @@ -269,15 +239,15 @@ padding-left: 8px; } - & > & { + & > & { float: left; } - & > &:not(:first-child):not(:last-child) > .@{btnClassName} { + & > &:not(:first-child):not(:last-child) > .@{btnClassName} { border-radius: 0; } - & > &:first-child:not(:last-child) { + & > &:first-child:not(:last-child) { > .@{btnClassName}:last-child { border-bottom-right-radius: 0; border-top-right-radius: 0; @@ -285,7 +255,7 @@ } } - & > &:last-child:not(:first-child) > .@{btnClassName}:first-child { + & > &:last-child:not(:first-child) > .@{btnClassName}:first-child { border-bottom-left-radius: 0; border-top-left-radius: 0; padding-left: 8px; diff --git a/components/calendar/Constants.js b/components/calendar/Constants.tsx similarity index 100% rename from components/calendar/Constants.js rename to components/calendar/Constants.tsx diff --git a/components/calendar/Header.jsx b/components/calendar/Header.tsx similarity index 65% rename from components/calendar/Header.jsx rename to components/calendar/Header.tsx index ae67307108..f2f3a4d9d4 100644 --- a/components/calendar/Header.jsx +++ b/components/calendar/Header.tsx @@ -1,11 +1,33 @@ -import React, { PropTypes, Component } from 'react'; +import { PropTypes } from 'react'; +import * as React from 'react'; import { PREFIX_CLS } from './Constants'; import Select from '../select'; import { Group, Button } from '../radio'; +const Option = Select.Option; function noop() {} -class Header extends Component { +export default class Header extends React.Component { + static defaultProps = { + prefixCls: `${PREFIX_CLS}-header`, + yearSelectOffset: 10, + yearSelectTotal: 20, + onValueChange: noop, + onTypeChange: noop, + }; + + static propTypes = { + value: PropTypes.object, + locale: PropTypes.object, + yearSelectOffset: PropTypes.number, + yearSelectTotal: PropTypes.number, + onValueChange: PropTypes.func, + onTypeChange: PropTypes.func, + prefixCls: PropTypes.string, + selectPrefixCls: PropTypes.string, + type: PropTypes.string, + }; + getYearSelectElement(year) { const { yearSelectOffset, yearSelectTotal, locale, prefixCls, fullscreen } = this.props; const start = year - yearSelectOffset; @@ -19,16 +41,18 @@ class Header extends Component { return ( ); } + getMonthSelectElement(month) { const props = this.props; const months = props.locale.format.months; @@ -43,35 +67,39 @@ class Header extends Component { ); } - onYearChange(year) { + + onYearChange = (year) => { const newValue = this.props.value.clone(); newValue.setYear(parseInt(year, 10)); this.props.onValueChange(newValue); } - onMonthChange(month) { + onMonthChange = (month) => { const newValue = this.props.value.clone(); newValue.setMonth(parseInt(month, 10)); this.props.onValueChange(newValue); } - onTypeChange(e) { + + onTypeChange = (e) => { this.props.onTypeChange(e.target.value); } + render() { const { type, value, prefixCls, locale } = this.props; const yearSelect = this.getYearSelectElement(value.getYear()); const monthSelect = type === 'date' ? this.getMonthSelectElement(value.getMonth()) : null; const typeSwitch = ( - + @@ -79,32 +107,10 @@ class Header extends Component { return (
- { yearSelect } - { monthSelect } - { typeSwitch } + {yearSelect} + {monthSelect} + {typeSwitch}
); } } - -Header.propTypes = { - value: PropTypes.object, - locale: PropTypes.object, - yearSelectOffset: PropTypes.number, - yearSelectTotal: PropTypes.number, - onValueChange: PropTypes.func, - onTypeChange: PropTypes.func, - prefixCls: PropTypes.string, - selectPrefixCls: PropTypes.string, - type: PropTypes.string, -}; - -Header.defaultProps = { - prefixCls: `${PREFIX_CLS}-header`, - yearSelectOffset: 10, - yearSelectTotal: 20, - onValueChange: noop, - onTypeChange: noop, -}; - -export default Header; diff --git a/components/calendar/demo/basic.md b/components/calendar/demo/basic.md index 248ca79447..712ace6d61 100644 --- a/components/calendar/demo/basic.md +++ b/components/calendar/demo/basic.md @@ -1,10 +1,18 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- -- order: 0 +## zh-CN 一个通用的日历面板,支持年/月切换。 ---- + +## en-US + +A basic calendar component with Year/Month switch. ````jsx import { Calendar } from 'antd'; diff --git a/components/calendar/demo/card.md b/components/calendar/demo/card.md index d5420cf8d5..bb04bc0173 100644 --- a/components/calendar/demo/card.md +++ b/components/calendar/demo/card.md @@ -1,10 +1,17 @@ -# 卡片模式 +--- +order: 10 +title: + zh-CN: 卡片模式 + en-US: Card +--- -- order: 10 +## zh-CN 用于嵌套在空间有限的容器中。 ---- +## en-US + +Nested inside a container element for rendering in limited space. ````jsx import { Calendar } from 'antd'; diff --git a/components/calendar/demo/custom-render.md b/components/calendar/demo/custom-render.md index cebd745585..e0470ee64c 100644 --- a/components/calendar/demo/custom-render.md +++ b/components/calendar/demo/custom-render.md @@ -1,10 +1,17 @@ -# 自定义渲染 +--- +order: 1 +title: + zh-CN: 自定义渲染 + en-US: Custom Render +--- -- order: 1 +## zh-CN 用 `dateCellRender` 和 `monthCellRender` 函数来自定义需要渲染的数据。 ---- +## en-US + +This component can be rendered by using `dateCellRender` and `monthCellRender` with the data you need. ````jsx import { Calendar } from 'antd'; @@ -19,6 +26,7 @@ function monthCellRender(value) { ReactDOM.render( + dateCellRender={dateCellRender} monthCellRender={monthCellRender} + /> , mountNode); ```` diff --git a/components/calendar/demo/locale.md b/components/calendar/demo/locale.md index f292bbc540..48b1fdb476 100644 --- a/components/calendar/demo/locale.md +++ b/components/calendar/demo/locale.md @@ -1,10 +1,17 @@ -# 国际化 +--- +order: 4 +title: + zh-CN: 国际化 + en-US: locale +--- -- order: 4 +## zh-CN 通过 `locale` 配置时区、语言等, 默认支持 en_US, zh_CN ---- +## en-US + +To set the properties like time zone, language and etc. en_US, zh_CN are supported by default. ````jsx import { Calendar } from 'antd'; diff --git a/components/calendar/demo/notice-calendar.md b/components/calendar/demo/notice-calendar.md index 32ee6e6adf..f6edbb52ae 100644 --- a/components/calendar/demo/notice-calendar.md +++ b/components/calendar/demo/notice-calendar.md @@ -1,10 +1,17 @@ -# 通知事项日历演示 - -- order: 2 - -一个复杂的应用实例。 - --- +order: 2 +title: + zh-CN: 通知事项日历演示 + en-US: A demo of Notice Calendar +--- + +## zh-CN + +一个复杂的应用示例。 + +## en-US + +A complex application. ````jsx import { Calendar } from 'antd'; @@ -14,23 +21,23 @@ function getListData(value) { switch (value.getDayOfMonth()) { case 8: listData = [ - { type: 'warn', content: '这里是警告事项.' }, - { type: 'normal', content: '这里是普通事项.' } + { type: 'warning', content: '这里是警告事项.' }, + { type: 'normal', content: '这里是普通事项.' }, ]; break; case 10: listData = [ - { type: 'warn', content: '这里是警告事项.' }, + { type: 'warning', content: '这里是警告事项.' }, { type: 'normal', content: '这里是普通事项.' }, - { type: 'error', content: '这里是错误事项.' } + { type: 'error', content: '这里是错误事项.' }, ]; break; case 15: listData = [ - { type: 'warn', content: '这里是警告事项.' }, + { type: 'warning', content: '这里是警告事项.' }, { type: 'normal', content: '这里是普通事项好长啊。。....' }, { type: 'error', content: '这里是错误事项.' }, { type: 'error', content: '这里是错误事项.' }, { type: 'error', content: '这里是错误事项.' }, - { type: 'error', content: '这里是错误事项.' } + { type: 'error', content: '这里是错误事项.' }, ]; break; default: } @@ -38,7 +45,7 @@ function getListData(value) { } function dateCellRender(value) { - let listData = getListData(value); + const listData = getListData(value); return (
    { @@ -53,7 +60,6 @@ function dateCellRender(value) { ); } - function getMonthData(value) { if (value.getMonth() === 8) { return 1394; @@ -73,7 +79,6 @@ ReactDOM.render( , mountNode); ```` - ````css .events { line-height: 24px; @@ -98,7 +103,7 @@ ReactDOM.render( margin-right: 4px; } -.event-warn { +.event-warning { color: #fac450; } @@ -107,7 +112,7 @@ ReactDOM.render( } .event-error { - color: #f60; + color: #f50; } .notes-month { diff --git a/components/calendar/index.en-US.md b/components/calendar/index.en-US.md new file mode 100644 index 0000000000..825eae4b60 --- /dev/null +++ b/components/calendar/index.en-US.md @@ -0,0 +1,33 @@ +--- +category: Components +type: Views +cols: 1 +title: Calendar +--- + +When to use container for displaying data in calendar form. + +## When to use + +When data is in the form of date, such as schedule, timetable, prices calendar, Lunar calendar. This component also supports Year/Month switch. + +## API + +```html + +``` + +| Property | Description | Type | Default | +|--------------|----------------|----------|--------------| +| value | set date | Date | current date | +| defaultValue | set default date | Date | current date | +| mode | can be set to month or year | string | month | +| fullscreen | to set whether full-screen display | bool | true | +| dateCellRender | to set the way of renderer the date cell| function([GregorianCalendar](https://github.com/yiminghe/gregorian-calendar/))| - | +| monthCellRender | to set the way of renderer the month cell | function([GregorianCalendar](https://github.com/yiminghe/gregorian-calendar/)) | - | +| locale | set locale | object | [defualt](https://github.com/ant-design/ant-design/issues/424) | +| onPanelChange| the callback when panel change | function(date, mode) | - | diff --git a/components/calendar/index.jsx b/components/calendar/index.tsx similarity index 57% rename from components/calendar/index.jsx rename to components/calendar/index.tsx index 6be2b5e8e0..677977aaab 100644 --- a/components/calendar/index.jsx +++ b/components/calendar/index.tsx @@ -1,18 +1,48 @@ -import React, { PropTypes, Component } from 'react'; +import { PropTypes } from 'react'; +import * as React from 'react'; import GregorianCalendar from 'gregorian-calendar'; -import zhCN from './locale/zh_CN'; +import defaultLocale from './locale/zh_CN'; import FullCalendar from 'rc-calendar/lib/FullCalendar'; import { PREFIX_CLS } from './Constants'; import Header from './Header'; +import assign from 'object-assign'; function noop() { return null; } function zerofixed(v) { - if (v < 10) return `0${v}`; + if (v < 10) { + return `0${v}`; + } return `${v}`; } -class Calendar extends Component { +export default class Calendar extends React.Component { + static defaultProps = { + monthCellRender: noop, + dateCellRender: noop, + locale: {}, + fullscreen: true, + prefixCls: PREFIX_CLS, + onPanelChange: noop, + mode: 'month', + }; + + static propTypes = { + monthCellRender: PropTypes.func, + dateCellRender: PropTypes.func, + fullscreen: PropTypes.bool, + locale: PropTypes.object, + prefixCls: PropTypes.string, + className: PropTypes.string, + style: PropTypes.object, + onPanelChange: PropTypes.func, + value: PropTypes.instanceOf(Date), + }; + + static contextTypes = { + antLocale: PropTypes.object, + }; + constructor(props) { super(props); this.state = { @@ -20,19 +50,35 @@ class Calendar extends Component { mode: props.mode, }; } + parseDateFromValue(value) { - const date = new GregorianCalendar(this.props.locale); + const date = new GregorianCalendar(this.getLocale()); date.setTime(+value); return date; } + componentWillReceiveProps(nextProps) { if ('value' in nextProps) { this.setState({ - value: this.parseDateFromValue(nextProps.value) + value: this.parseDateFromValue(nextProps.value), }); } } - monthCellRender(value, locale) { + + getLocale = () => { + const props = this.props; + let locale = defaultLocale; + const context = this.context; + if (context && context.antLocale && context.antLocale.Calendar) { + locale = context.antLocale.Calendar; + } + // 统一合并为完整的 Locale + const result = assign({}, locale, props.locale); + result.lang = assign({}, locale.lang, props.locale.lang); + return result; + } + + monthCellRender = (value, locale) => { const prefixCls = this.props.prefixCls; const month = value.getMonth(); return ( @@ -46,7 +92,8 @@ class Calendar extends Component {
); } - dateCellRender(value) { + + dateCellRender = (value) => { const prefixCls = this.props.prefixCls; return (
@@ -59,24 +106,28 @@ class Calendar extends Component {
); } - setValue(value) { + + setValue = (value) => { if (!('value' in this.props) && this.state.value !== value) { this.setState({ value }); } this.props.onPanelChange(value, this.state.mode); } - setType(type) { + + setType = (type) => { const mode = (type === 'date') ? 'month' : 'year'; if (this.state.mode !== mode) { this.setState({ mode }); this.props.onPanelChange(this.state.value, mode); } } + render() { const props = this.props; const { value, mode } = this.state; - const { locale, prefixCls, style, className, fullscreen } = props; + const { prefixCls, style, className, fullscreen } = props; const type = (mode === 'year') ? 'month' : 'date'; + const locale = this.getLocale(); let cls = className || ''; if (fullscreen) { @@ -91,8 +142,9 @@ class Calendar extends Component { value={value} locale={locale.lang} prefixCls={prefixCls} - onTypeChange={this.setType.bind(this)} - onValueChange={this.setValue.bind(this)} /> + onTypeChange={this.setType} + onValueChange={this.setValue} + /> + monthCellRender={this.monthCellRender} + dateCellRender={this.dateCellRender} + /> ); } } - -Calendar.propTypes = { - monthCellRender: PropTypes.func, - dateCellRender: PropTypes.func, - fullscreen: PropTypes.bool, - locale: PropTypes.object, - prefixCls: PropTypes.string, - className: PropTypes.string, - style: PropTypes.object, - onPanelChange: PropTypes.func, - value: PropTypes.instanceOf(Date), -}; - -Calendar.defaultProps = { - monthCellRender: noop, - dateCellRender: noop, - locale: zhCN, - fullscreen: true, - prefixCls: PREFIX_CLS, - onPanelChange: noop, - mode: 'month', -}; - -export default Calendar; diff --git a/components/calendar/index.md b/components/calendar/index.zh-CN.md similarity index 93% rename from components/calendar/index.md rename to components/calendar/index.zh-CN.md index 4cefbf7516..520abba811 100644 --- a/components/calendar/index.md +++ b/components/calendar/index.zh-CN.md @@ -1,10 +1,9 @@ -# Calendar - -- category: Components -- type: 展示 -- chinese: 日历 -- cols: 1 - +--- +category: Components +type: Views +chinese: 日历 +cols: 1 +english: Calendar --- 按照日历形式展示数据的容器。 diff --git a/components/calendar/locale/en_US.js b/components/calendar/locale/en_US.tsx similarity index 100% rename from components/calendar/locale/en_US.js rename to components/calendar/locale/en_US.tsx diff --git a/components/calendar/locale/ru_RU.tsx b/components/calendar/locale/ru_RU.tsx new file mode 100644 index 0000000000..aea2dd9a41 --- /dev/null +++ b/components/calendar/locale/ru_RU.tsx @@ -0,0 +1 @@ +module.exports = require('../../date-picker/locale/ru_RU'); diff --git a/components/calendar/locale/zh_CN.js b/components/calendar/locale/zh_CN.tsx similarity index 100% rename from components/calendar/locale/zh_CN.js rename to components/calendar/locale/zh_CN.tsx diff --git a/style/components/calendar.less b/components/calendar/style/index.less similarity index 99% rename from style/components/calendar.less rename to components/calendar/style/index.less index 2a5cd92181..f464844c72 100644 --- a/style/components/calendar.less +++ b/components/calendar/style/index.less @@ -1,3 +1,5 @@ +@import "../../style/themes/default"; + @full-calendar-prefix-cls: ant-fullcalendar; .@{full-calendar-prefix-cls} { diff --git a/components/calendar/style/index.tsx b/components/calendar/style/index.tsx new file mode 100644 index 0000000000..184ccc0ac9 --- /dev/null +++ b/components/calendar/style/index.tsx @@ -0,0 +1,6 @@ +import '../../style/index.less'; +import './index.less'; + +// style dependencies +import '../../select/style'; +import '../../radio/style'; diff --git a/components/card/demo/basic.md b/components/card/demo/basic.md new file mode 100644 index 0000000000..e51f291432 --- /dev/null +++ b/components/card/demo/basic.md @@ -0,0 +1,26 @@ +--- +order: 0 +title: + zh-CN: 典型卡片 + en-US: Basic card +--- + +## zh-CN + +包含标题、内容、操作区域。 + +## en-US + +A basic card containing a title, content and an extra corner content. + +````jsx +import { Card } from 'antd'; + +ReactDOM.render( + More} style={{ width: 300 }}> +

Card content

+

Card content

+

Card content

+
+, mountNode); +```` diff --git a/components/card/demo/border-less.md b/components/card/demo/border-less.md new file mode 100644 index 0000000000..c83805c1dc --- /dev/null +++ b/components/card/demo/border-less.md @@ -0,0 +1,28 @@ +--- +order: 1 +title: + zh-CN: 无边框 + en-US: No border +--- + +## zh-CN + +在灰色背景上使用无边框的卡片。 + +## en-US + +A borderless card on a gray background. + +````jsx +import { Card } from 'antd'; + +ReactDOM.render( +
+ +

Card content

+

Card content

+

Card content

+
+
+, mountNode); +```` diff --git a/components/card/demo/grid.md b/components/card/demo/grid.md new file mode 100644 index 0000000000..f087fd453c --- /dev/null +++ b/components/card/demo/grid.md @@ -0,0 +1,45 @@ +--- +order: 4 +title: + zh-CN: 栅格卡片 + en-US: Grid card +--- + +## zh-CN + +在系统概览页面常常和栅格进行配合。 + +## en-US + +Cards usually cooperate with grid layout in overview page. + +````jsx +import { Card, Col, Row } from 'antd'; + +ReactDOM.render( +
+ + + Card content + + + Card content + + + Card content + + +
+, mountNode); +```` + +````css +/* Increase grid spacing of 16px */ +.code-box-demo .ant-row { + margin-left: -8px; + margin-right: -8px; +} +.code-box-demo .ant-row > div { + padding: 0 8px; +} +```` diff --git a/components/card/demo/loading.md b/components/card/demo/loading.md new file mode 100644 index 0000000000..d40365d2a8 --- /dev/null +++ b/components/card/demo/loading.md @@ -0,0 +1,24 @@ +--- +order: 5 +title: + zh-CN: 预加载的卡片 + en-US: Loading card +--- + +## zh-CN + +数据读入前会有文本块样式。 + +## en-US + +Shows a loading indicator while the contents of the card are being fetched. + +````jsx +import { Card } from 'antd'; + +ReactDOM.render( + + Whatever content + +, mountNode); +```` diff --git a/components/card/demo/no-padding.md b/components/card/demo/no-padding.md new file mode 100644 index 0000000000..177ff78c03 --- /dev/null +++ b/components/card/demo/no-padding.md @@ -0,0 +1,43 @@ +--- +order: 3 +title: + zh-CN: 更灵活的内容展示 + en-US: Customized content +--- + +## zh-CN + +可以调整默认边距,设定宽度。 + +## en-US + +Customizing default width and margin. + + +````jsx +import { Card } from 'antd'; + +ReactDOM.render( + +
+ example +
+
+

Europe Street beat

+

www.instagram.com

+
+
+, mountNode); +```` + +````css +.custom-image img { + display: block; +} +.custom-card { + padding: 10px 16px; +} +.custom-card p { + color: #999; +} +```` diff --git a/components/card/demo/simple.md b/components/card/demo/simple.md new file mode 100644 index 0000000000..054179848b --- /dev/null +++ b/components/card/demo/simple.md @@ -0,0 +1,26 @@ +--- +order: 2 +title: + zh-CN: 简洁卡片 + en-US: Simple card +--- + +## zh-CN + +只包含内容区域。 + +## en-US + +A simple card only containing a content area. + +````jsx +import { Card } from 'antd'; + +ReactDOM.render( + +

Card content

+

Card content

+

Card content

+
+, mountNode); +```` diff --git a/components/card/index.en-US.md b/components/card/index.en-US.md new file mode 100644 index 0000000000..357214aedd --- /dev/null +++ b/components/card/index.en-US.md @@ -0,0 +1,25 @@ +--- +category: Components +type: Views +title: Card +cols: 1 +--- + +Simple rectangular container + +## When to use + +A card can be used to display content related to a single subject. The content can consist of multiple elements of varying type and size. + +## API + +```html +Card content +``` + +| Property | Description | Type | Default | +|----------|----------------|----------|--------------| +| title | Card title | React.Element | - | +| extra | Content to render in the top-right corner of the card | React.Element | - | +| bordered | Toggles rendering of the border around the card | Boolean | true | +| bodyStyle | Inline style to apply to the card content | Object | - | diff --git a/components/card/index.tsx b/components/card/index.tsx new file mode 100644 index 0000000000..094495151c --- /dev/null +++ b/components/card/index.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import classNames from 'classnames'; +import splitObject from '../_util/splitObject'; + +interface CardProps { + title?: React.ReactNode; + extra?: React.ReactNode; + bordered?: boolean; + bodyStyle?: React.CSSProperties; + style?: React.CSSProperties; + loading?: boolean; + children?: any; +} + +export default (props: CardProps) => { + const [{ + prefixCls = 'ant-card', className, extra, bodyStyle, + title, loading, bordered = true, + }, others] = splitObject(props, + ['prefixCls', 'className', 'children', 'extra', 'bodyStyle', 'title', 'loading', 'bordered']); + let children = props.children; + const classString = classNames({ + [prefixCls]: true, + [className]: !!className, + [`${prefixCls}-loading`]: loading, + [`${prefixCls}-bordered`]: bordered, + }); + + if (loading) { + children = ( +
+

████████████████████████

+

██████ ███████████████████

+

██████████████ ██████████

+

█████ ██████ █████████████

+

███████████ ██████████ ███

+
+ ); + } + + const head = title ? ( +
+

{title}

+
+ ) : null; + + return ( +
+ {head} + {extra ?
{extra}
: null} +
{children}
+
+ ); +}; diff --git a/components/card/index.zh-CN.md b/components/card/index.zh-CN.md new file mode 100644 index 0000000000..58dffef938 --- /dev/null +++ b/components/card/index.zh-CN.md @@ -0,0 +1,26 @@ +--- +category: Components +type: Views +title: Card +subtitle: 卡片 +cols: 1 +--- + +通用卡片容器。 + +## 何时使用 + +最基础的卡片容器,可承载文字、列表、图片、段落,常用于后台概览页面。 + +## API + +```html +卡片内容 +``` + +| 参数 | 说明 | 类型 | 默认值 | +|----------|----------------|----------|--------------| +| title | 卡片标题 | React.Element | - | +| extra | 卡片右上角的操作区域 | React.Element | - | +| bordered | 是否有边框 | Boolean | true | +| bodyStyle | 内容区域自定义样式 | Object | - | diff --git a/components/card/style/index.less b/components/card/style/index.less new file mode 100644 index 0000000000..b54a324923 --- /dev/null +++ b/components/card/style/index.less @@ -0,0 +1,66 @@ +@import "../../style/themes/default"; + +@card-prefix-cls: ant-card; + +.@{card-prefix-cls} { + background: #fff; + border-radius: @border-radius-sm; + font-size: @font-size-base; + position: relative; + overflow: hidden; + transition: all .3s; + + &:hover { + box-shadow: @box-shadow-base; + border-color: #eee; + } + + &-bordered { + border: 1px solid @border-color-base; + border-color: @border-color-split; + } + + &-head { + height: 48px; + line-height: 48px; + border-bottom: 1px solid @border-color-split; + padding: 0 24px; + + &-title { + font-size: 14px; + display: inline-block; + text-overflow: ellipsis; + width: 100%; + overflow: hidden; + white-space: nowrap; + } + } + + &-extra { + position: absolute; + right: 24px; + top: 14px; + } + + &-body { + padding: 24px; + } + + &-loading &-body { + letter-spacing: -2px; + color: #eee; + font-size: 0.75rem; + } + + &-loading &-body p { + word-break: break-all; + line-height: 10px; + margin: 5px 0 0; + height: 10px; + border-radius: @border-radius-base; + overflow: hidden; + display: inline-block; + user-select: none; + color: #f3f5f8; + } +} diff --git a/components/card/style/index.tsx b/components/card/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/card/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/carousel/demo/autoplay.md b/components/carousel/demo/autoplay.md index 4072b2023c..30e2888c79 100644 --- a/components/carousel/demo/autoplay.md +++ b/components/carousel/demo/autoplay.md @@ -1,16 +1,23 @@ -# 自动切换 +--- +order: 3 +title: + zh-CN: 自动切换 + en-US: Scroll automatically +--- -- order: 3 +## zh-CN 定时切换下一张。 ---- +## en-US + +Timing of scrolling to the next card/picture. ````jsx import { Carousel } from 'antd'; ReactDOM.render( - +

1

2

3

@@ -18,4 +25,3 @@ ReactDOM.render(
, mountNode); ```` - diff --git a/components/carousel/demo/basic.md b/components/carousel/demo/basic.md index 6a0f569957..2412636802 100644 --- a/components/carousel/demo/basic.md +++ b/components/carousel/demo/basic.md @@ -1,10 +1,17 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- -- order: 0 +## zh-CN 最简单的用法。 ---- +## en-US + +Basic usage. ````jsx import { Carousel } from 'antd'; diff --git a/components/carousel/demo/fade.md b/components/carousel/demo/fade.md index 05a7619ef0..4501743189 100644 --- a/components/carousel/demo/fade.md +++ b/components/carousel/demo/fade.md @@ -1,10 +1,17 @@ -# 渐显 +--- +order: 2 +title: + zh-CN: 渐显 + en-US: Fade in +--- -- order: 2 +## zh-CN 切换效果为渐显。 ---- +## en-US + +Slides use fade for transition. ````jsx import { Carousel } from 'antd'; diff --git a/components/carousel/demo/vertical.md b/components/carousel/demo/vertical.md index 172ca7e171..d13fc60f15 100644 --- a/components/carousel/demo/vertical.md +++ b/components/carousel/demo/vertical.md @@ -1,10 +1,17 @@ -# 垂直 +--- +order: 1 +title: + zh-CN: 垂直 + en-US: Vertical +--- -- order: 1 +## zh-CN 垂直显示。 ---- +## en-US + +Vertical pagination. ````jsx import { Carousel } from 'antd'; diff --git a/components/carousel/index.en-US.md b/components/carousel/index.en-US.md new file mode 100644 index 0000000000..2cf6538e04 --- /dev/null +++ b/components/carousel/index.en-US.md @@ -0,0 +1,41 @@ +--- +category: Components +type: Views +title: Carousel +--- + +A carousel component. Scales with its container. + +## When to use + +- When there is a group of content on the same level. +- When there is insufficient content space, it can be used to save space in the form of a resolving door. +- Commonly used in a carousel for a group of pictures/cards. + +## API + +| Property | Description | Type | Default | +|------------------|----------------------------------------------|----------|---------------------------------| +| effect | Animation effect, either `scrollx` or `fade` | String | scrollx | +| dots | Should we show the dots at the bottom of the gallery | Boolean | true | +| vertical | Whether to use a vertical display | Boolean | false | +| autoplay | Whether to scroll automatically | Boolean | false | +| easing | Transition name | String | linear | +| beforeChange | Callback function called before the current index changes | function(from, to) | +| afterChange | Callback function called after the current index changes | function(current) | + +For more info on the parameters, refer to the https://github.com/akiran/react-slick + + diff --git a/components/carousel/index.jsx b/components/carousel/index.jsx deleted file mode 100644 index a994d46d90..0000000000 --- a/components/carousel/index.jsx +++ /dev/null @@ -1,48 +0,0 @@ -// matchMedia polyfill for -// https://github.com/WickyNilliams/enquire.js/issues/82 -if (typeof window !== 'undefined') { - const matchMediaPolyfill = function matchMediaPolyfill() { - return { - matches: false, - addListener() { - }, - removeListener() { - }, - }; - }; - window.matchMedia = window.matchMedia || matchMediaPolyfill; -} - -import Carousel from 'react-slick'; -import React from 'react'; -import assign from 'object-assign'; - -const AntCarousel = React.createClass({ - getDefaultProps() { - return { - dots: true, - arrows: false, - }; - }, - render() { - let props = assign({}, this.props); - - if (props.effect === 'fade') { - props.fade = true; - props.draggable = false; - } - - let className = 'ant-carousel'; - if (props.vertical) { - className = `${className} ant-carousel-vertical`; - } - - return ( -
- -
- ); - } -}); - -export default AntCarousel; diff --git a/components/carousel/index.tsx b/components/carousel/index.tsx new file mode 100644 index 0000000000..721af47654 --- /dev/null +++ b/components/carousel/index.tsx @@ -0,0 +1,66 @@ +// matchMedia polyfill for +// https://github.com/WickyNilliams/enquire.js/issues/82 +import assign from 'object-assign'; +if (typeof window !== 'undefined') { + const matchMediaPolyfill = function matchMediaPolyfill() { + return { + matches: false, + addListener() { + }, + removeListener() { + }, + }; + }; + window.matchMedia = window.matchMedia || matchMediaPolyfill; +} + +import SlickCarousel from 'react-slick'; +import * as React from 'react'; + +export type CarouselEffect = 'scrollx' | 'fade' +// Carousel +export interface CarouselProps { + /** 动画效果函数,可取 scrollx, fade */ + effect?: CarouselEffect; + /** 是否显示面板指示点 */ + dots?: SlickCarouselboolean; + /** 垂直显示 */ + vertical?: boolean; + /** 是否自动切换 */ + autoplay?: boolean; + /** 动画效果 */ + easing?: string; + /** 切换面板的回调 */ + beforeChange?: (from: number, to: number) => void; + /** 切换面板的回调 */ + afterChange?: (current: number) => void; + /** 行内样式 */ + style?: React.CSSProperties; +} + +export default class Carousel extends React.Component { + static defaultProps = { + dots: true, + arrows: false, + }; + + render() { + let props = assign({}, this.props); + + if (props.effect === 'fade') { + props.fade = true; + props.draggable = false; + } + + let className = 'ant-carousel'; + if (props.vertical) { + className = `${className} ant-carousel-vertical`; + } + + return ( +
+ +
+ ); + } +} diff --git a/components/carousel/index.md b/components/carousel/index.zh-CN.md similarity index 94% rename from components/carousel/index.md rename to components/carousel/index.zh-CN.md index 471bf17d65..a208db80a5 100644 --- a/components/carousel/index.md +++ b/components/carousel/index.zh-CN.md @@ -1,9 +1,8 @@ -# Carousel - -- category: Components -- chinese: 走马灯 -- type: 展示 - +--- +category: Components +type: Views +title: Carousel +subtitle: 走马灯 --- 旋转木马,一组轮播的区域。 diff --git a/style/components/carousel/slick-theme.less b/components/carousel/style/index.less similarity index 58% rename from style/components/carousel/slick-theme.less rename to components/carousel/style/index.less index c963854aaf..cca53e16d6 100644 --- a/style/components/carousel/slick-theme.less +++ b/components/carousel/style/index.less @@ -1,7 +1,98 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; + .ant-carousel { + .slick-slider { + position: relative; + display: block; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-touch-callout: none; + user-select: none; + -ms-touch-action: pan-y; + touch-action: pan-y; + -webkit-tap-highlight-color: transparent; + } + .slick-list { + position: relative; + overflow: hidden; + display: block; + margin: 0; + padding: 0; + + &:focus { + outline: none; + } + + &.dragging { + cursor: pointer; + cursor: hand; + } + } + .slick-slider .slick-track, + .slick-slider .slick-list { + transform: translate3d(0, 0, 0); + } + + .slick-track { + position: relative; + left: 0; + top: 0; + display: block; + + &:before, + &:after { + content: ""; + display: table; + } + + &:after { + clear: both; + } + + .slick-loading & { + visibility: hidden; + } + } + .slick-slide { + float: left; + height: 100%; + min-height: 1px; + [dir="rtl"] & { + float: right; + } + img { + display: block; + } + &.slick-loading img { + display: none; + } + + display: none; + + &.dragging img { + pointer-events: none; + } + } + + .slick-initialized .slick-slide { + display: block; + } + + .slick-loading .slick-slide { + visibility: hidden; + } + + .slick-vertical .slick-slide { + display: block; + height: auto; + border: 1px solid transparent; + } + .slick-arrow.slick-hidden { + display: none; + } // Arrows - .slick-prev, .slick-next { position: absolute; @@ -99,6 +190,7 @@ line-height: 20px; text-align: center; color: gray; + transition: all .3s; opacity: 0.25; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @@ -113,11 +205,9 @@ } .ant-carousel-vertical { - .slick-slider { padding-bottom: 0; } - .slick-dots { width: 20px; bottom: auto; diff --git a/components/carousel/style/index.tsx b/components/carousel/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/carousel/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/cascader/demo/basic.md b/components/cascader/demo/basic.md index 445f039f4f..2c093c06ff 100644 --- a/components/cascader/demo/basic.md +++ b/components/cascader/demo/basic.md @@ -1,10 +1,17 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- -- order: 0 +## zh-CN 省市区级联。 ---- +## en-US + +Cascade selection box for selecting province/city/district. ````jsx import { Cascader } from 'antd'; @@ -38,6 +45,6 @@ function onChange(value) { } ReactDOM.render( - + , mountNode); ```` diff --git a/components/cascader/demo/change-on-select.md b/components/cascader/demo/change-on-select.md index 0f2c2773ee..718937d79d 100644 --- a/components/cascader/demo/change-on-select.md +++ b/components/cascader/demo/change-on-select.md @@ -1,10 +1,17 @@ -# 选择即改变 +--- +order: 5 +title: + zh-CN: 选择即改变 + en-US: Change on select +--- -- order: 5 +## zh-CN 这种交互允许只选中父级选项。 ---- +## en-US + +Allow only select parent options. ````jsx import { Cascader } from 'antd'; diff --git a/components/cascader/demo/custom-render.md b/components/cascader/demo/custom-render.md new file mode 100644 index 0000000000..953b7e53f0 --- /dev/null +++ b/components/cascader/demo/custom-render.md @@ -0,0 +1,70 @@ +--- +order: 8 +title: + zh-CN: 自定义已选项 + en-US: Custom render +--- + +## zh-CN + +例如给最后一项加上邮编链接。 + +## en-US + +For instance, add an external link after the selected value. + +````jsx +import { Cascader } from 'antd'; + +const options = [{ + value: 'zhejiang', + label: '浙江', + children: [{ + value: 'hangzhou', + label: '杭州', + children: [{ + value: 'xihu', + label: '西湖', + code: 752100, + }], + }], +}, { + value: 'jiangsu', + label: '江苏', + children: [{ + value: 'nanjing', + label: '南京', + children: [{ + value: 'zhonghuamen', + label: '中华门', + code: 453400, + }], + }], +}]; + +function handleAreaClick(e, label, option) { + e.stopPropagation(); + console.log('点击了', label, option); +} + +const displayRender = (labels, selectedOptions) => labels.map((label, i) => { + const option = selectedOptions[i]; + if (i === labels.length - 1) { + return ( + + {label} ( handleAreaClick(e, label, option)}>{option.code}) + + ); + } + return {label} / ; +}); + +ReactDOM.render( + +, mountNode); +```` diff --git a/components/cascader/demo/custom-trigger.md b/components/cascader/demo/custom-trigger.md index ddc07c01a1..5d4ef4d5d9 100644 --- a/components/cascader/demo/custom-trigger.md +++ b/components/cascader/demo/custom-trigger.md @@ -1,10 +1,17 @@ -# 可以自定义显示 +--- +order: 1 +title: + zh-CN: 可以自定义显示 + en-US: Custom trigger +--- -- order: 1 +## zh-CN 切换按钮和结果分开。 ---- +## en-US + +Separate trigger button and result. ````jsx import { Cascader } from 'antd'; diff --git a/components/cascader/demo/default-value.md b/components/cascader/demo/default-value.md index db4ea4eee0..c454a673a4 100644 --- a/components/cascader/demo/default-value.md +++ b/components/cascader/demo/default-value.md @@ -1,10 +1,17 @@ -# 默认值 +--- +order: 0 +title: + zh-CN: 默认值 + en-US: Default value +--- -- order: 0 +## zh-CN 默认值通过数组的方式指定。 ---- +## en-US + +Specifies default value by an array. ````jsx import { Cascader } from 'antd'; diff --git a/components/cascader/demo/disabled-option.md b/components/cascader/demo/disabled-option.md index e0a1aaf77c..9731d6b668 100644 --- a/components/cascader/demo/disabled-option.md +++ b/components/cascader/demo/disabled-option.md @@ -1,10 +1,17 @@ -# 禁用选项 +--- +order: 4 +title: + zh-CN: 禁用选项 + en-US: Disabled option +--- -- order: 4 +## zh-CN 通过指定 options 里的 `disabled` 字段。 ---- +## en-US + +Disable option by specifying the `disabled` property in `options`. ````jsx import { Cascader } from 'antd'; diff --git a/components/cascader/demo/hover.md b/components/cascader/demo/hover.md index f9e51caa30..6acfe66d15 100644 --- a/components/cascader/demo/hover.md +++ b/components/cascader/demo/hover.md @@ -1,10 +1,17 @@ -# 移入展开 +--- +order: 2 +title: + zh-CN: 移入展开 + en-US: Hover +--- -- order: 2 +## zh-CN 通过移入展开下级菜单,点击完成选择。 ---- +## en-US + +Hover to expand sub menu, click to select option. ````jsx import { Cascader } from 'antd'; @@ -44,6 +51,7 @@ function displayRender(label) { ReactDOM.render( + displayRender={displayRender} onChange={onChange} + /> , mountNode); ```` diff --git a/components/cascader/demo/size.md b/components/cascader/demo/size.md index 8e8c2123a3..1e26b2fcde 100644 --- a/components/cascader/demo/size.md +++ b/components/cascader/demo/size.md @@ -1,10 +1,17 @@ -# 大小 +--- +order: 7 +title: + zh-CN: 大小 + en-US: Size +--- -- order: 7 +## zh-CN 不同大小的级联选择器。 ---- +## en-US + +Cascade selection box of different sizes. ````jsx import { Cascader } from 'antd'; diff --git a/components/cascader/index.en-US.md b/components/cascader/index.en-US.md new file mode 100644 index 0000000000..4d8930f98d --- /dev/null +++ b/components/cascader/index.en-US.md @@ -0,0 +1,38 @@ +--- +category: Components +type: Form Controls +title: Cascader +--- + +Cascade selection box. + + +## When to use + +- When you need to select from a set of associated data set. Such as province/city/district, company level, things classification. +- When selecting from a large data set, with multi-stage classification separated for easy selection. +- chooses cascade items in one float layer for better user experience. + +## API + +```html + +``` + +| Property | Description | Type | Default | +|------|------|------|--------| +| options | data options of cascade | object | - | +| defaultValue | initial selected value | array |[] | +| value | selected value | array | - | +| onChange | callback when finishing cascader select | `function(value, selectedOptions)` | - | +| displayRender | render function of displaying selected options | `function(label, selectedOptions)` | `label => label.join(' / ')` | +| style | additional style | string | - | +| className | additional css class | string | - | +| popupClassName | additional className of popup overlay | string | - | +| popupPlacement | use preset popup align config from builtinPlacements:`bottomLeft` `bottomRight` `topLeft` `topRight` | string | `bottomLeft` | +| placeholder | input placeholder | string | 'Please select' | +| size | input size, one of `large` `default` `small` | string | `default` | +| disabled | whether disabled select | boolean | false | +| allowClear | whether allow clear | boolean | true | +| expandTrigger | expand current item when click or hover, one of 'click' 'hover' | string | 'click' | +| changeOnSelect | change value on each selection if set to true, see above demo for details | boolean | false | diff --git a/components/cascader/index.jsx b/components/cascader/index.jsx deleted file mode 100644 index 6017d3a015..0000000000 --- a/components/cascader/index.jsx +++ /dev/null @@ -1,118 +0,0 @@ -import React from 'react'; -import Cascader from 'rc-cascader'; -import Input from '../input'; -import Icon from '../icon'; -import arrayTreeFilter from 'array-tree-filter'; -import classNames from 'classnames'; - -class AntCascader extends React.Component { - constructor(props) { - super(props); - this.state = { - value: props.value || props.defaultValue || [], - popupVisible: false, - }; - [ - 'handleChange', - 'handlePopupVisibleChange', - 'setValue', - 'getLabel', - 'clearSelection', - ].forEach((method) => this[method] = this[method].bind(this)); - } - componentWillReceiveProps(nextProps) { - if ('value' in nextProps) { - this.setState({ value: nextProps.value || [] }); - } - } - handleChange(value, selectedOptions) { - this.setValue(value, selectedOptions); - } - handlePopupVisibleChange(popupVisible) { - this.setState({ popupVisible }); - this.props.onPopupVisibleChange(popupVisible); - } - setValue(value, selectedOptions = []) { - if (!('value' in this.props)) { - this.setState({ value }); - } - this.props.onChange(value, selectedOptions); - } - getLabel() { - const { options, displayRender } = this.props; - const label = arrayTreeFilter(options, (o, level) => o.value === this.state.value[level]) - .map(o => o.label); - return displayRender(label); - } - clearSelection(e) { - e.preventDefault(); - e.stopPropagation(); - this.setValue([]); - this.setState({ popupVisible: false }); - } - render() { - const { prefixCls, children, placeholder, size, disabled, - className, style, allowClear, ...otherProps } = this.props; - const sizeCls = classNames({ - 'ant-input-lg': size === 'large', - 'ant-input-sm': size === 'small', - }); - const clearIcon = (allowClear && !disabled && this.state.value.length > 0) ? - : null; - const arrowCls = classNames({ - [`${prefixCls}-picker-arrow`]: true, - [`${prefixCls}-picker-arrow-expand`]: this.state.popupVisible, - }); - const pickerCls = classNames({ - [className]: !!className, - [`${prefixCls}-picker`]: true, - [`${prefixCls}-picker-disabled`]: disabled, - }); - - // Fix bug of https://github.com/facebook/react/pull/5004 - delete otherProps.onChange; - - return ( - - {children || - - - {clearIcon} - - - } - - ); - } -} - -AntCascader.defaultProps = { - prefixCls: 'ant-cascader', - placeholder: '请选择', - transitionName: 'slide-up', - popupPlacement: 'bottomLeft', - onChange() {}, - options: [], - displayRender(label) { - return label.join(' / '); - }, - disabled: false, - allowClear: true, - onPopupVisibleChange() {}, -}; - -export default AntCascader; diff --git a/components/cascader/index.tsx b/components/cascader/index.tsx new file mode 100644 index 0000000000..0ed6df1c8f --- /dev/null +++ b/components/cascader/index.tsx @@ -0,0 +1,186 @@ +import * as React from 'react'; +import RcCascader from 'rc-cascader'; +import Input from '../input'; +import Icon from '../icon'; +import arrayTreeFilter from 'array-tree-filter'; +import classNames from 'classnames'; +import splitObject from '../_util/splitObject'; +import omit from 'object.omit'; + +export interface CascaderOptionType { + value: string; + label: string; + disabled?: boolean; + children?: Array; +} + +export type CascaderExpandTrigger = 'click' | 'hover' +export interface CascaderProps { + /** 可选项数据源 */ + options: Array; + /** 默认的选中项 */ + defaultValue?: Array; + /** 指定选中项 */ + value?: Array; + /** 选择完成后的回调 */ + onChange?: (value: string, selectedOptions?: Array) => void; + /** 选择后展示的渲染函数 */ + displayRender?: (label: Array, selectedOptions?: Array) => React.ReactNode; + /** 自定义样式 */ + style?: React.CSSProperties; + /** 自定义类名 */ + className?: string; + /** 自定义浮层类名 */ + popupClassName?: string; + /** 浮层预设位置:`bottomLeft` `bottomRight` `topLeft` `topRight` */ + popupPlacement?: string; + /** 输入框占位文本*/ + placeholder?: string; + /** 输入框大小,可选 `large` `default` `small` */ + size?: string; + /** 禁用*/ + disabled?: boolean; + /** 是否支持清除*/ + allowClear?: boolean; + /** 次级菜单的展开方式,可选 'click' 和 'hover' */ + expandTrigger?: CascaderExpandTrigger; + /** 当此项为 true 时,点选每级菜单选项值都会发生变化 */ + changeOnSelect?: boolean; + /** 浮层可见变化时回调 */ + onPopupVisibleChange?: (popupVisible: boolean) => void; +} + +export default class Cascader extends React.Component { + static defaultProps = { + prefixCls: 'ant-cascader', + placeholder: 'Please select', + transitionName: 'slide-up', + popupPlacement: 'bottomLeft', + onChange() {}, + options: [], + displayRender: label => label.join(' / '), + disabled: false, + allowClear: true, + onPopupVisibleChange() {}, + }; + + constructor(props) { + super(props); + let value; + if ('value' in props) { + value = props.value; + } else if ('defaultValue' in props) { + value = props.defaultValue; + } + this.state = { + value: value || [], + popupVisible: false, + }; + } + + componentWillReceiveProps(nextProps) { + if ('value' in nextProps) { + this.setState({ value: nextProps.value || [] }); + } + } + + handleChange = (value, selectedOptions) => { + this.setValue(value, selectedOptions); + } + + handlePopupVisibleChange = (popupVisible) => { + this.setState({ popupVisible }); + this.props.onPopupVisibleChange(popupVisible); + } + + setValue = (value, selectedOptions = []) => { + if (!('value' in this.props)) { + this.setState({ value }); + } + this.props.onChange(value, selectedOptions); + } + + getLabel() { + const { options, displayRender } = this.props; + const selectedOptions = arrayTreeFilter(options, (o, level) => o.value === this.state.value[level]); + const label = selectedOptions.map(o => o.label); + return displayRender(label, selectedOptions); + } + + clearSelection = (e) => { + e.preventDefault(); + e.stopPropagation(); + this.setValue([]); + this.setState({ popupVisible: false }); + } + + render() { + const props = this.props; + const [{ prefixCls, children, placeholder, size, disabled, + className, style, allowClear }, otherProps] = splitObject(props, + ['prefixCls', 'children', 'placeholder', 'size', 'disabled', 'className', 'style', 'allowClear']); + + const sizeCls = classNames({ + 'ant-input-lg': size === 'large', + 'ant-input-sm': size === 'small', + }); + const clearIcon = (allowClear && !disabled && this.state.value.length > 0) ? + : null; + const arrowCls = classNames({ + [`${prefixCls}-picker-arrow`]: true, + [`${prefixCls}-picker-arrow-expand`]: this.state.popupVisible, + }); + const pickerCls = classNames({ + [className]: !!className, + [`${prefixCls}-picker`]: true, + [`${prefixCls}-picker-disabled`]: disabled, + }); + + // Fix bug of https://github.com/facebook/react/pull/5004 + // and https://fb.me/react-unknown-prop + const inputProps = omit(otherProps, [ + 'onChange', + 'options', + 'popupPlacement', + 'transitionName', + 'displayRender', + 'onPopupVisibleChange', + 'changeOnSelect', + 'expandTrigger', + 'popupVisible', + 'getPopupContainer', + 'loadData', + 'popupClassName', + ]); + + return ( + + {children || + + 0 ? null : placeholder} + className={`${prefixCls}-input ${sizeCls}`} + value="" + disabled={disabled} + readOnly + /> + {this.getLabel()} + {clearIcon} + + + } + + ); + } +} diff --git a/components/cascader/index.md b/components/cascader/index.zh-CN.md similarity index 77% rename from components/cascader/index.md rename to components/cascader/index.zh-CN.md index 3d5462ecbc..30029be949 100644 --- a/components/cascader/index.md +++ b/components/cascader/index.zh-CN.md @@ -1,9 +1,8 @@ -# Cascader - -- category: Components -- chinese: 级联选择 -- type: 表单 - +--- +category: Components +type: Form Controls +chinese: 级联选择 +english: Cascader --- 级联选择框。 @@ -27,7 +26,7 @@ | defaultValue | 默认的选中项 | array |[] | | value | 指定选中项 | array | - | | onChange | 选择完成后的回调 | `function(value, selectedOptions)` | - | -| displayRender | 选择后展示的渲染函数 | `function(label)`` | `function(label) { return label.join(' / ') }` | +| displayRender | 选择后展示的渲染函数 | `function(label, selectedOptions)` | `label => label.join(' / ')` | | style | 自定义样式 | string | - | | className | 自定义类名 | string | - | | popupClassName | 自定义浮层类名 | string | - | @@ -36,3 +35,5 @@ | size | 输入框大小,可选 `large` `default` `small` | string | `default` | | disabled | 禁用 | boolean | false | | allowClear | 是否支持清除 | boolean | true | +| expandTrigger | 次级菜单的展开方式,可选 'click' 和 'hover' | string | 'click' | +| changeOnSelect | 当此项为 true 时,点选每级菜单选项值都会发生变化,具体见上面的演示 | boolean | false | diff --git a/style/components/cascader.less b/components/cascader/style/index.less similarity index 73% rename from style/components/cascader.less rename to components/cascader/style/index.less index 584e1164b0..5e12146ff8 100644 --- a/style/components/cascader.less +++ b/components/cascader/style/index.less @@ -1,27 +1,49 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; + @cascader-prefix-cls: ant-cascader; .@{cascader-prefix-cls} { font-size: @font-size-base; - &-input { - width: 172px; + &-input.ant-input { display: block; cursor: pointer; + width: 100%; + z-index: 1; } &-picker { position: relative; display: inline-block; cursor: pointer; vertical-align: middle; + font-size: @font-size-base; + background-color: #fff; + border-radius: @border-radius-base; &-disabled { cursor: not-allowed; } + &-label { + position: absolute; + left: 0; + height: 20px; + line-height: 20px; + top: 50%; + margin-top: -10px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + width: 100%; + padding: 0 12px 0 8px; + z-index: 1; + } + &-clear { opacity: 0; position: absolute; right: 8px; - z-index: 1; + z-index: 2; background: #fff; top: 50%; font-size: 12px; @@ -44,6 +66,7 @@ // arrow &-arrow { position: absolute; + z-index: 1; top: 50%; right: 8px; width: 12px; @@ -58,21 +81,20 @@ &&-expand { .ie-rotate(2); &:before { - .rotate(180deg); + transform: rotate(180deg); } } } } &-menus { font-size: 12px; - overflow: hidden; background: #fff; position: absolute; + z-index: @zindex-dropdown; border: 1px solid @border-color-base; border-radius: @border-radius-base; box-shadow: @box-shadow-base; white-space: nowrap; - height: 180px; &-empty, &-hidden { display: none; @@ -105,17 +127,22 @@ padding: 0; border-right: 1px solid @border-color-split; overflow: auto; + &:first-child { + border-radius: @border-radius-base 0 0 @border-radius-base; + } &:last-child { border-right-color: transparent; margin-right: -1px; + border-radius: 0 @border-radius-base @border-radius-base 0; + } + &:only-child { + border-radius: @border-radius-base; } } &-menu-item { - padding: 7px 16px; + padding: 7px 26px 7px 16px; cursor: pointer; white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; transition: all 0.3s ease; &:hover { background: tint(@primary-color, 90%); @@ -127,17 +154,17 @@ background: transparent; } } - &-active { - background: tint(@primary-color, 80%); + &-active:not(&-disabled) { + &, &:hover { - background: tint(@primary-color, 80%); + background-color: @background-color-base; + font-weight: bold; } } &-expand { position: relative; &:after { - content: '\e600'; - font-family: 'anticon'; + .iconfont-font("\e600"); .iconfont-size-under-12px(8px); color: #999; position: absolute; diff --git a/components/cascader/style/index.tsx b/components/cascader/style/index.tsx new file mode 100644 index 0000000000..0e54062b35 --- /dev/null +++ b/components/cascader/style/index.tsx @@ -0,0 +1,5 @@ +import '../../style/index.less'; +import './index.less'; + +// style dependencies +import '../../input/style'; diff --git a/components/checkbox/Group.jsx b/components/checkbox/Group.jsx deleted file mode 100644 index e9b353856d..0000000000 --- a/components/checkbox/Group.jsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import Checkbox from './index'; - -export default React.createClass({ - getDefaultProps() { - return { - options: [], - defaultValue: [], - onChange() {}, - }; - }, - propTypes: { - defaultValue: React.PropTypes.array, - value: React.PropTypes.array, - options: React.PropTypes.array.isRequired, - onChange: React.PropTypes.func, - }, - getInitialState() { - const props = this.props; - let value; - if ('value' in props) { - value = props.value; - } else if ('defaultValue' in props) { - value = props.defaultValue; - } - return { value }; - }, - componentWillReceiveProps(nextProps) { - if ('value' in nextProps) { - this.setState({ - value: nextProps.value || [], - }); - } - }, - toggleOption(option) { - const optionIndex = this.state.value.indexOf(option); - const value = [...this.state.value]; - if (optionIndex === - 1) { - value.push(option); - } else { - value.splice(optionIndex, 1); - } - if (!('value' in this.props)) { - this.setState({ value }); - } - this.props.onChange(value); - }, - render() { - const options = this.props.options; - return ( -
- { - options.map(option => - - ) - } -
- ); - }, -}); diff --git a/components/checkbox/Group.tsx b/components/checkbox/Group.tsx new file mode 100644 index 0000000000..24cc6fb05b --- /dev/null +++ b/components/checkbox/Group.tsx @@ -0,0 +1,103 @@ +import * as React from 'react'; +import Checkbox from './index'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; + +export interface CheckboxOptionType { + label: string; + value: string; + disabled?: boolean; +} + +export interface CheckboxGroupProps { + /** 默认选中的选项 */ + defaultValue?: Array; + /** 指定选中的选项 */ + value?: Array; + /** 指定可选项 */ + options?: Array | Array; + /** 变化时回调函数 */ + onChange?: (checkedValue: Array) => void; + disabled?: boolean; + style?: React.CSSProperties; +} + +export interface CheckboxGroupState { + value: any; +} + +export default class CheckboxGroup extends React.Component { + static defaultProps = { + options: [], + defaultValue: [], + onChange() {}, + }; + static propTypes = { + defaultValue: React.PropTypes.array, + value: React.PropTypes.array, + options: React.PropTypes.array.isRequired, + onChange: React.PropTypes.func, + }; + constructor(props) { + super(props); + let value; + if ('value' in props) { + value = props.value || []; + } else if ('defaultValue' in props) { + value = props.defaultValue || []; + } + this.state = { value }; + } + componentWillReceiveProps(nextProps) { + if ('value' in nextProps) { + this.setState({ + value: nextProps.value || [], + }); + } + } + shouldComponentUpdate(...args) { + return PureRenderMixin.shouldComponentUpdate.apply(this, args); + } + getOptions() { + const { options } = this.props; + return options.map(option => { + if (typeof option === 'string') { + return { + label: option, + value: option, + }; + } + return option; + }); + } + toggleOption = (option) => { + const optionIndex = this.state.value.indexOf(option.value); + const value = [...this.state.value]; + if (optionIndex === - 1) { + value.push(option.value); + } else { + value.splice(optionIndex, 1); + } + if (!('value' in this.props)) { + this.setState({ value }); + } + this.props.onChange(value); + } + render() { + const options = this.getOptions(); + return ( +
+ { + options.map(option => + this.toggleOption(option)} + className="ant-checkbox-group-item" key={option.value} + > + {option.label} + + ) + } +
+ ); + } +} diff --git a/components/checkbox/demo/basic.md b/components/checkbox/demo/basic.md index b642019a4b..6db6141b01 100644 --- a/components/checkbox/demo/basic.md +++ b/components/checkbox/demo/basic.md @@ -1,10 +1,17 @@ -# 基本用法 +--- +order: 0 +title: + zh-CN: 基本用法 + en-US: Basic +--- -- order: 0 +## zh-CN 简单的 checkbox。 ---- +## en-US + +Basic usage of checkbox. ````jsx import { Checkbox } from 'antd'; @@ -13,8 +20,7 @@ function onChange(e) { console.log(`checked = ${e.target.checked}`); } -ReactDOM.render(, mountNode); +ReactDOM.render( + Checkbox +, mountNode); ```` diff --git a/components/checkbox/demo/controller.md b/components/checkbox/demo/controller.md index ff636c333e..812a330757 100644 --- a/components/checkbox/demo/controller.md +++ b/components/checkbox/demo/controller.md @@ -1,10 +1,17 @@ -# 和外部组件通信 +--- +order: 2 +title: + zh-CN: 受控的 Checkbox + en-US: Controlled Checkbox +--- -- order: 2 +## zh-CN 联动 checkbox。 ---- +## en-US + +Communicated with other components. ````jsx import { Checkbox, Button } from 'antd'; @@ -13,33 +20,35 @@ const App = React.createClass({ getInitialState() { return { checked: true, - disabled: false + disabled: false, }; }, render() { - const label = `${this.state.checked ? '选中' : '取消'}-${this.state.disabled ? '不可用' : '可用'}`; + const label = `${this.state.checked ? 'Checked' : 'Unchecked'}-${this.state.disabled ? 'Disabled' : 'Enabled'}`; return (

- -

-

- - -

-
+ + {label} + +

+

+ + +

+ ); }, toggleChecked() { @@ -53,7 +62,7 @@ const App = React.createClass({ this.setState({ checked: e.target.checked, }); - } + }, }); ReactDOM.render(, mountNode); diff --git a/components/checkbox/demo/disable.md b/components/checkbox/demo/disable.md index 372cca946c..ae52df4f92 100644 --- a/components/checkbox/demo/disable.md +++ b/components/checkbox/demo/disable.md @@ -1,10 +1,17 @@ -# 不可用 +--- +order: 1 +title: + zh-CN: 不可用 + en-US: Disabled +--- -- order: 1 +## zh-CN checkbox 不可用。 ---- +## en-US + +Disabled checkbox. ````jsx import { Checkbox } from 'antd'; diff --git a/components/checkbox/demo/group.md b/components/checkbox/demo/group.md index b7dacc4f69..3148a8b06e 100644 --- a/components/checkbox/demo/group.md +++ b/components/checkbox/demo/group.md @@ -1,10 +1,17 @@ -# Checkbox 组 - -- order: 3 - -方便的从数组生成 Checkbox 组。若需要 label 和 value 分离请直接使用 Checkbox。 - --- +order: 3 +title: + zh-CN: Checkbox 组 + en-US: Checkbox Group +--- + +## zh-CN + +方便的从数组生成 Checkbox 组。 + +## en-US + +Generate a group of checkboxes from an array. ````jsx import { Checkbox } from 'antd'; @@ -14,7 +21,25 @@ function onChange(checkedValues) { console.log('checked = ', checkedValues); } +const plainOptions = ['Apple', 'Pear', 'Orange']; +const options = [ + { label: 'Apple', value: 'Apple' }, + { label: 'Pear', value: 'Pear' }, + { label: 'Orange', value: 'Orange' }, +]; +const optionsWithDisabled = [ + { label: 'Apple', value: 'Apple' }, + { label: 'Pear', value: 'Pear' }, + { label: 'Orange', value: 'Orange', disabled: false }, +]; + ReactDOM.render( - +
+ +
+ +
+ +
, mountNode); ```` diff --git a/components/checkbox/index.en-US.md b/components/checkbox/index.en-US.md new file mode 100644 index 0000000000..b8add7624e --- /dev/null +++ b/components/checkbox/index.en-US.md @@ -0,0 +1,31 @@ +--- +category: Components +type: Form Controls +title: Checkbox +--- + +Checkbox. + +## When To Use + +- Used for selecting multiple values from several options. +- If you use only one checkbox, it is the same as using Switch to toggle between two states. The difference is that Switch will trigger the state change directly, but Checkbox just marks the state as changed and this needs to be submitted. + +## API + +### Checkbox + +| Property | Description | Type | Default | +|----------|------------------|----------|--------| +| checked | Specifies whether the checkbox is selected. | Boolean | false | +| defaultChecked | Specifies the initial state: whether or not the checkbox is selected. | Boolean | false | +| onChange | The callback function that is triggered when the state changes. | Function(e:Event) | - | + +### Checkbox Group + +| Property | Description | Type | Default | +|----------|------------------|----------|--------| +| defaultValue | Default selected value | Array | [] | +| value | Used for setting the currently selected value. | Array | [] | +| options | Specifies options | Array | [] | +| onChange | The callback function that is triggered when the state changes. | Function(checkedValue) | - | diff --git a/components/checkbox/index.jsx b/components/checkbox/index.jsx deleted file mode 100644 index b04ab10b61..0000000000 --- a/components/checkbox/index.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import RcCheckbox from 'rc-checkbox'; -import React from 'react'; -import Group from './Group'; - -const Checkbox = React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-checkbox' - }; - }, - render() { - return ; - } -}); - -Checkbox.Group = Group; - -export default Checkbox; diff --git a/components/checkbox/index.md b/components/checkbox/index.md deleted file mode 100644 index 7da10d7401..0000000000 --- a/components/checkbox/index.md +++ /dev/null @@ -1,33 +0,0 @@ -# Checkbox - -- category: Components -- chinese: 多选框 -- type: 表单 - ---- - -多选框。 - -## 何时使用 - -- 在一组可选项中进行多项选择时; -- 单独使用可以表示两种状态之间的切换,和 `switch` 类似。区别在于切换 `switch` 会直接触发状态改变,而 `checkbox` 一般用于状态标记,需要和提交操作配合。 - -## API - -### Checkbox - -| 参数 | 说明 | 类型 | 可选值 |默认值 | -|-----------|------------------------------------------|------------|-------|--------| -| checked | 指定当前是否选中 | boolean | | false | -| defaultChecked | 初始是否选中 | boolean | | false | -| onChange | 变化时回调函数 | Function(e:Event) | | | | - -### Checkbox Group - -| 参数 | 说明 | 类型 | 可选值 | 默认值 | -|-----------|------------------------------------------|------------|---------|--------| -| defaultValue | 默认选中的选项 | array | | [] | -| value | 指定选中的选项| array | | [] | -| options | 指定可选项 | array | | [] | -| onChange | 变化时回调函数 | Function(checkedValue) | | | | diff --git a/components/checkbox/index.tsx b/components/checkbox/index.tsx new file mode 100644 index 0000000000..e13e42fdf2 --- /dev/null +++ b/components/checkbox/index.tsx @@ -0,0 +1,43 @@ +import RcCheckbox from 'rc-checkbox'; +import * as React from 'react'; +import CheckboxGroup from './Group'; +import classNames from 'classnames'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import splitObject from '../_util/splitObject'; + +export interface CheckboxProps { + /** 指定当前是否选中 */ + checked?: boolean; + /** 初始是否选中 */ + defaultChecked?: boolean; + /** 变化时回调函数 */ + onChange?: React.FormEventHandler; + style?: React.CSSProperties; + disabled?: boolean; + className?: string; +} + +export default class Checkbox extends React.Component { + static Group = CheckboxGroup; + static defaultProps = { + prefixCls: 'ant-checkbox', + }; + shouldComponentUpdate(...args) { + return PureRenderMixin.shouldComponentUpdate.apply(this, args); + } + render() { + const [{ prefixCls, style, children, className }, restProps] = splitObject( + this.props, ['prefixCls', 'style', 'children', 'className'] + ); + const classString = classNames({ + [className]: !!className, + [`${prefixCls}-wrapper`]: true, + }); + return ( + + ); + } +} diff --git a/components/checkbox/index.zh-CN.md b/components/checkbox/index.zh-CN.md new file mode 100644 index 0000000000..a73b9d9453 --- /dev/null +++ b/components/checkbox/index.zh-CN.md @@ -0,0 +1,32 @@ +--- +category: Components +chinese: 多选框 +type: Form Controls +english: Checkbox +--- + +多选框。 + +## 何时使用 + +- 在一组可选项中进行多项选择时; +- 单独使用可以表示两种状态之间的切换,和 `switch` 类似。区别在于切换 `switch` 会直接触发状态改变,而 `checkbox` 一般用于状态标记,需要和提交操作配合。 + +## API + +### Checkbox + +| 参数 | 说明 | 类型 | 默认值 | +|----------|------------------|----------|--------| +| checked | 指定当前是否选中 | Boolean | false | +| defaultChecked | 初始是否选中 | Boolean | false | +| onChange | 变化时回调函数 | Function(e:Event) | - | + +### Checkbox Group + +| 参数 | 说明 | 类型 | 默认值 | +|----------|------------------|----------|--------| +| defaultValue | 默认选中的选项 | Array | [] | +| value | 指定选中的选项| Array | [] | +| options | 指定可选项 | Array | [] | +| onChange | 变化时回调函数 | Function(checkedValue) | - | diff --git a/components/checkbox/style/index.less b/components/checkbox/style/index.less new file mode 100644 index 0000000000..fa88d365aa --- /dev/null +++ b/components/checkbox/style/index.less @@ -0,0 +1,4 @@ +@import "../../style/themes/default"; +@import "./mixin"; + +.antCheckboxFn(); diff --git a/components/checkbox/style/index.tsx b/components/checkbox/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/checkbox/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/checkbox/style/mixin.less b/components/checkbox/style/mixin.less new file mode 100644 index 0000000000..92c529cdcd --- /dev/null +++ b/components/checkbox/style/mixin.less @@ -0,0 +1,199 @@ +@import "../../style/mixins/index"; + +.antCheckboxFn(@checkbox-prefix-cls: ant-checkbox) { + @checkbox-inner-prefix-cls: ~"@{checkbox-prefix-cls}-inner"; + // 一般状态 + .@{checkbox-prefix-cls} { + white-space: nowrap; + cursor: pointer; + outline: none; + display: inline-block; + line-height: 1; + position: relative; + vertical-align: middle; + + &:hover, + &-focused { + .@{checkbox-inner-prefix-cls} { + border-color: #bcbcbc; + } + } + + &-inner { + &:after { + transform: rotate(45deg) scale(0); + position: absolute; + left: 4px; + top: 1px; + display: table; + width: 5px; + height: 8px; + border: 2px solid #fff; + border-top: 0; + border-left: 0; + content: ' '; + transition: all .1s @ease-in-back; + } + + position: relative; + top: 0; + left: 0; + display: inline-block; + width: 14px; + height: 14px; + border-width: 1px; + border-style: solid; + border-radius: 3px; + border-color: @border-color-base; + background-color: #fff; + transition: border-color 0.1s @ease-in-out-back, background-color 0.1s @ease-in-out-back; + } + + &-input { + position: absolute; + left: 0; + z-index: 1; + cursor: pointer; + .opacity(0); + top: 0; + bottom: 0; + right: 0; + width: 100%; + height: 100%; + } + } + + // 半选状态 + .@{checkbox-prefix-cls}-indeterminate { + .@{checkbox-inner-prefix-cls} { + border-color: @primary-color; + background-color: @primary-color; + &:after { + content: ' '; + transform: scale(1); + position: absolute; + left: 2px; + top: 5px; + width: 8px; + height: 1px; + } + } + &:hover { + .@{checkbox-inner-prefix-cls} { + border-color: @primary-color; + } + } + } + // 选中状态 + .@{checkbox-prefix-cls}-checked { + + &:hover { + .@{checkbox-inner-prefix-cls} { + border-color: @primary-color; + } + } + + .@{checkbox-inner-prefix-cls} { + border-color: @primary-color; + background-color: @primary-color; + + &:after { + transform: rotate(45deg) scale(1); + position: absolute; + left: 4px; + top: 1px; + display: table; + width: 5px; + height: 8px; + border: 2px solid #fff; + border-top: 0; + border-left: 0; + content: ' '; + transition: all .2s @ease-out-back .1s; + } + } + } + + .@{checkbox-prefix-cls}-disabled { + &.@{checkbox-prefix-cls}-checked { + &:hover { + .@{checkbox-inner-prefix-cls} { + border-color: @border-color-base; + } + } + + .@{checkbox-inner-prefix-cls} { + background-color: #f3f3f3; + border-color: @border-color-base; + + &:after { + animation-name: none; + border-color: #ccc; + } + } + } + + &:hover { + .@{checkbox-inner-prefix-cls} { + border-color: @border-color-base; + } + } + + .@{checkbox-inner-prefix-cls} { + border-color: @border-color-base; + background-color: #f3f3f3; + &:after { + animation-name: none; + border-color: #f3f3f3; + } + } + + .@{checkbox-inner-prefix-cls}-input { + cursor: default; + } + + & + span { + color: #ccc; + cursor: @cursor-disabled; + } + } + + .@{checkbox-prefix-cls}-wrapper { + cursor: pointer; + font-size: @font-size-base; + display: inline-block; + & + & { + margin-left: 8px; + } + } + + .@{checkbox-prefix-cls}-wrapper + span, + .@{checkbox-prefix-cls} + span { + margin-left: 8px; + margin-right: 8px; + } + + .@{checkbox-prefix-cls}-group { + font-size: @font-size-base; + &-item { + display: inline-block; + } + } + + @ie8: \0screen; + + // IE8 hack for https://github.com/ant-design/ant-design/issues/2148 + @media @ie8 { + .@{checkbox-prefix-cls}-checked .@{checkbox-prefix-cls}-inner:before, + .@{checkbox-prefix-cls}-checked .@{checkbox-prefix-cls}-inner:after { + .iconfont-font("\e62e"); + font-weight: bold; + font-size: 8px; + border: 0; + color: #fff; + left: 2px; + top: 3px; + position: absolute; + } + } +} diff --git a/components/col/index.js b/components/col/index.tsx similarity index 100% rename from components/col/index.js rename to components/col/index.tsx diff --git a/components/col/style/index.tsx b/components/col/style/index.tsx new file mode 100644 index 0000000000..875df4fa55 --- /dev/null +++ b/components/col/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import '../../layout/style/index.less'; diff --git a/components/collapse/demo/accordion.md b/components/collapse/demo/accordion.md index e5432515f1..33a4143c4b 100644 --- a/components/collapse/demo/accordion.md +++ b/components/collapse/demo/accordion.md @@ -1,10 +1,17 @@ -# 手风琴 +--- +order: 1 +title: + zh-CN: 手风琴 + en-US: Accordion +--- -- order: 1 +## zh-CN 手风琴,每次只打开一个tab。默认打开第一个。 ---- +## en-US + +Accordion mode, only one panel can be expanded at a time. The first panel will be expanded by default. ````jsx import { Collapse } from 'antd'; diff --git a/components/collapse/demo/basic.md b/components/collapse/demo/basic.md index f3b95cdd3b..72eaab4ff2 100644 --- a/components/collapse/demo/basic.md +++ b/components/collapse/demo/basic.md @@ -1,10 +1,17 @@ -# 折叠面板 +--- +order: 0 +title: + zh-CN: 折叠面板 + en-US: Collapse +--- -- order: 0 +## zh-CN 可以同时展开多个面板,这个例子默认展开了第一个。 ---- +## en-US + +More than one panel can be expanded at a time, the first panel is initialized to be active in this case. ````jsx import { Collapse } from 'antd'; diff --git a/components/collapse/demo/mix.md b/components/collapse/demo/mix.md index ba376017f0..324e9f8c9b 100644 --- a/components/collapse/demo/mix.md +++ b/components/collapse/demo/mix.md @@ -1,10 +1,17 @@ -# 面板嵌套 +--- +order: 2 +title: + zh-CN: 面板嵌套 + en-US: Nested panel +--- -- order: 2 +## zh-CN 手风琴嵌套折叠面板。 ---- +## en-US + +`Collapse` is nested inside the `Accordion`. ````jsx import { Collapse } from 'antd'; diff --git a/components/collapse/index.en-US.md b/components/collapse/index.en-US.md new file mode 100644 index 0000000000..16d19710a6 --- /dev/null +++ b/components/collapse/index.en-US.md @@ -0,0 +1,30 @@ +--- +category: Components +type: Views +title: Collapse +--- + +A content area which can be collapsed and expanded. + +## When to use + +- Can be used to group or hide complex regions to keep the page clean. +- `Accordion` is a special kind of `Collapse`, which allows only one panel to be expanded at a time. + +## API + +### Collapse + +| Property | Description | Type | Default | +|----------|----------------|----------|--------------| +| activeKey | key of the active panel | Array or String | No default value. In `accordion` mode, it's the key of the first panel. | +| defaultActiveKey | key of the initialized active panel | String | - | +| onChange | a callback function, which can be executed when you switch the panels | Function | - | + +### Collapse.Panel + +| Property | Description | Type | Default | +|----------|----------------|----------|--------------| +| key | corresponds to the `activeKey` | String | - | +| header | title of the panel | React.Element or String | - | + diff --git a/components/collapse/index.jsx b/components/collapse/index.jsx deleted file mode 100644 index 38db75e087..0000000000 --- a/components/collapse/index.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import Collapse from 'rc-collapse'; -import React from 'react'; - -class AntCollapse extends React.Component { - render() { - return ; - } -} - -AntCollapse.defaultProps = { - prefixCls: 'ant-collapse', -}; - -AntCollapse.Panel = Collapse.Panel; - -export default AntCollapse; diff --git a/components/collapse/index.tsx b/components/collapse/index.tsx new file mode 100644 index 0000000000..d7e86f0638 --- /dev/null +++ b/components/collapse/index.tsx @@ -0,0 +1,37 @@ +import RcCollapse from 'rc-collapse'; +import * as React from 'react'; + +export interface CollapseProps { + activeKey?: Array | string; + /** 初始化选中面板的key */ + defaultActiveKey?: Array; + /** accordion 为 true 的时候,一次只可以打开一个面板 */ + accordion?: boolean; + /** 切换面板的回调 */ + onChange?: (key: string) => void; + style?: React.CSSProperties; +} + +export interface CollapsePanelProps { + /** 对应 activeKey */ + key: string; + /** 面板头内容 */ + header: React.ReactNode; + style?: React.CSSProperties; +} + +export class CollapsePanel extends React.Component { + +} + +export default class Collapse extends React.Component { + static Panel: typeof CollapsePanel = RcCollapse.Panel; + + static defaultProps = { + prefixCls: 'ant-collapse', + }; + + render() { + return ; + } +} diff --git a/components/collapse/index.md b/components/collapse/index.zh-CN.md similarity index 93% rename from components/collapse/index.md rename to components/collapse/index.zh-CN.md index 0a2c280bf3..49e447c795 100644 --- a/components/collapse/index.md +++ b/components/collapse/index.zh-CN.md @@ -1,9 +1,8 @@ -# Collapse - -- category: Components -- chinese: 折叠面板 -- type: 展示 - +--- +category: Components +type: Views +title: Collapse +subtitle: 折叠面板 --- 可以折叠/展开的内容区域。 diff --git a/style/components/collapse.less b/components/collapse/style/index.less similarity index 86% rename from style/components/collapse.less rename to components/collapse/style/index.less index bc8a6b6483..25f178e79e 100644 --- a/style/components/collapse.less +++ b/components/collapse/style/index.less @@ -1,3 +1,6 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; + @collapse-prefix-cls: ant-collapse; .collapse-close() { @@ -8,7 +11,7 @@ } .@{collapse-prefix-cls} { - background-color: #f4f4f4; + background-color: @background-color-base; border-radius: 3px; border: 1px solid @border-color-base; @@ -48,8 +51,11 @@ } } + &-anim-active { + transition: height .2s @ease-out; + } + &-content { - display: none; overflow: hidden; color: @text-color; padding: 0 16px; @@ -60,8 +66,8 @@ padding-bottom: 16px; } - &-active { - display: block; + &-inactive { + display: none; } } diff --git a/components/collapse/style/index.tsx b/components/collapse/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/collapse/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/common/openAnimation.js b/components/common/openAnimation.js deleted file mode 100644 index 80d857a055..0000000000 --- a/components/common/openAnimation.js +++ /dev/null @@ -1,45 +0,0 @@ -let velocity; -if (typeof document !== 'undefined' && typeof window !== 'undefined') { - velocity = require('velocity-animate'); -} - -function animate(node, show, transitionName, done) { - let ok; - - function complete() { - if (!ok) { - ok = true; - done(); - } - } - - // Fix safari flash bug - /*eslint-disable */ - node.style.display = show ? 'block' : 'none'; - /*eslint-enable */ - velocity(node, transitionName, { - duration: 240, - complete, - easing: 'easeInOutQuad' - }); - return { - stop() { - velocity(node, 'finish'); - complete(); - } - }; -} - -const animation = { - enter(node, done) { - return animate(node, false, 'slideDown', done); - }, - leave(node, done) { - return animate(node, true, 'slideUp', done); - }, - appear(node, done) { - return animate(node, false, 'slideDown', done); - }, -}; - -module.exports = animation; diff --git a/components/date-picker/Calendar.tsx b/components/date-picker/Calendar.tsx new file mode 100644 index 0000000000..ce7939bad1 --- /dev/null +++ b/components/date-picker/Calendar.tsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +import CalendarLocale from 'rc-calendar/lib/locale/zh_CN'; +import RcCalendar from 'rc-calendar'; + +export default class Calendar extends React.Component { + static defaultProps = { + locale: CalendarLocale, + prefixCls: 'ant-calendar', + }; + + render() { + return ; + } +} diff --git a/components/date-picker/PickerMixin.jsx b/components/date-picker/PickerMixin.jsx deleted file mode 100644 index 837fba5e01..0000000000 --- a/components/date-picker/PickerMixin.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import objectAssign from 'object-assign'; -import defaultLocale from './locale/zh_CN'; -import DateTimeFormat from 'gregorian-calendar-format'; -import GregorianCalendar from 'gregorian-calendar'; - -export default { - getLocale() { - // 统一合并为完整的 Locale - let locale = objectAssign({}, defaultLocale, this.props.locale); - locale.lang = objectAssign({}, defaultLocale.lang, this.props.locale.lang); - return locale; - }, - - getFormatter() { - const formats = this.formats = this.formats || {}; - let format = this.props.format; - if (formats[format]) { - return formats[format]; - } - formats[format] = new DateTimeFormat(format, this.getLocale().lang.format); - return formats[format]; - }, - - parseDateFromValue(value) { - if (value) { - if (typeof value === 'string') { - return this.getFormatter().parse(value, { locale: this.getLocale() }); - } else if (value instanceof Date) { - let date = new GregorianCalendar(this.getLocale()); - date.setTime(+value); - return date; - } - } - return value; - }, - - // remove input readonly warning - handleInputChange() { - }, - - toggleOpen(e) { - this.setState({ - open: e.open - }); - }, -}; diff --git a/components/date-picker/RangePicker.jsx b/components/date-picker/RangePicker.jsx deleted file mode 100644 index 57d1525a6d..0000000000 --- a/components/date-picker/RangePicker.jsx +++ /dev/null @@ -1,170 +0,0 @@ -import React from 'react'; -import GregorianCalendar from 'gregorian-calendar'; -import RangeCalendar from 'rc-calendar/lib/RangeCalendar'; -import DatePicker from 'rc-calendar/lib/Picker'; -import TimePicker from 'rc-time-picker'; -import classNames from 'classnames'; -import PickerMixin from './PickerMixin'; - -export default React.createClass({ - getDefaultProps() { - return { - defaultValue: [], - format: 'yyyy-MM-dd', - startPlaceholder: '开始日期', - endPlaceholder: '结束日期', - transitionName: 'slide-up', - popupStyle: {}, - onChange() { - }, - onOk() { - }, - locale: {}, - align: { - offset: [0, -9], - }, - open: false - }; - }, - getInitialState() { - const { value, defaultValue } = this.props; - const start = (value && value[0]) || defaultValue[0]; - const end = (value && value[1]) || defaultValue[1]; - return { - value: [ - this.parseDateFromValue(start), - this.parseDateFromValue(end) - ] - }; - }, - mixins: [PickerMixin], - componentWillReceiveProps(nextProps) { - if ('value' in nextProps) { - const value = nextProps.value || []; - const start = this.parseDateFromValue(value[0]); - const end = this.parseDateFromValue(value[1]); - this.setState({ - value: [start, end] - }); - } - }, - handleChange(value) { - if (!('value' in this.props)) { - this.setState({ value }); - } - const startTime = value[0] ? new Date(value[0].getTime()) : null; - const endTime = value[1] ? new Date(value[1].getTime()) : null; - this.props.onChange([startTime, endTime]); - }, - render() { - const locale = this.getLocale(); - // 以下两行代码 - // 给没有初始值的日期选择框提供本地化信息 - // 否则会以周日开始排 - let defaultCalendarValue = new GregorianCalendar(locale); - defaultCalendarValue.setTime(Date.now()); - - const { disabledDate, showTime, size, startPlaceholder, endPlaceholder, getCalendarContainer, - transitionName, disabled, popupStyle, align, style, onOk } = this.props; - const state = this.state; - - let timePicker = null; - - if (showTime) { - timePicker = (); - } - - const calendarClassName = classNames({ - ['ant-calendar-time']: this.props.showTime, - }); - - let pickerChangeHandler = { - onChange: this.handleChange, - }; - - let calendarHandler = { - onOk: this.handleChange, - }; - - if (timePicker) { - pickerChangeHandler.onChange = (value) => { - // Click clear button - if (value === null || value.length === 0) { - this.handleChange(value); - } - }; - } else { - calendarHandler = {}; - } - - const calendar = ( - - ); - - const pickerClass = classNames({ - 'ant-calendar-picker': true, - 'ant-calendar-picker-open': state.open - }); - - const pickerInputClass = classNames({ - 'ant-calendar-range-picker': true, - 'ant-input': true, - 'ant-input-lg': size === 'large', - 'ant-input-sm': size === 'small', - }); - - return ( - - { - ({ value }) => { - const start = value[0]; - const end = value[1]; - return ( - - - ~ - - - - ); - } - } - - ); - } -}); diff --git a/components/date-picker/RangePicker.tsx b/components/date-picker/RangePicker.tsx new file mode 100644 index 0000000000..5755828661 --- /dev/null +++ b/components/date-picker/RangePicker.tsx @@ -0,0 +1,160 @@ +import * as React from 'react'; +import GregorianCalendar from 'gregorian-calendar'; +import RangeCalendar from 'rc-calendar/lib/RangeCalendar'; +import RcDatePicker from 'rc-calendar/lib/Picker'; +import classNames from 'classnames'; +import Icon from '../icon'; + +export default class RangePicker extends React.Component { + static defaultProps = { + defaultValue: [], + }; + + constructor(props) { + super(props); + const { value, defaultValue, parseDateFromValue } = this.props; + const start = (value && value[0]) || defaultValue[0]; + const end = (value && value[1]) || defaultValue[1]; + this.state = { + value: [ + parseDateFromValue(start), + parseDateFromValue(end), + ], + }; + } + + componentWillReceiveProps(nextProps) { + if ('value' in nextProps) { + const value = nextProps.value || []; + const start = nextProps.parseDateFromValue(value[0]); + const end = nextProps.parseDateFromValue(value[1]); + this.setState({ + value: [start, end], + }); + } + } + + clearSelection = (e) => { + e.preventDefault(); + e.stopPropagation(); + this.setState({ value: [] }); + this.handleChange([]); + } + + handleChange = (value) => { + const props = this.props; + if (!('value' in props)) { + this.setState({ value }); + } + const startDate = value[0] ? new Date(value[0].getTime()) : null; + const endDate = value[1] ? new Date(value[1].getTime()) : null; + const startDateString = value[0] ? props.getFormatter().format(value[0]) : ''; + const endDateString = value[1] ? props.getFormatter().format(value[1]) : ''; + props.onChange([startDate, endDate], [startDateString, endDateString]); + } + + render() { + const props = this.props; + const locale = props.locale; + // 以下两行代码 + // 给没有初始值的日期选择框提供本地化信息 + // 否则会以周日开始排 + let defaultCalendarValue = new GregorianCalendar(locale); + defaultCalendarValue.setTime(Date.now()); + + const { disabledDate, showTime, getCalendarContainer, + transitionName, disabled, popupStyle, align, style, onOk } = this.props; + const state = this.state; + + const calendarClassName = classNames({ + 'ant-calendar-time': showTime, + }); + + // 需要选择时间时,点击 ok 时才触发 onChange + let pickerChangeHandler = { + onChange: this.handleChange, + }; + let calendarHandler = { + onOk: this.handleChange, + }; + if (props.timePicker) { + pickerChangeHandler.onChange = (value) => { + this.handleChange(value); + }; + } else { + calendarHandler = {}; + } + + const startPlaceholder = ('startPlaceholder' in this.props) + ? props.startPlaceholder : locale.lang.rangePlaceholder[0]; + const endPlaceholder = ('endPlaceholder' in props) + ? props.endPlaceholder : locale.lang.rangePlaceholder[1]; + + const calendar = ( + + ); + + const clearIcon = (!props.disabled && state.value && (state.value[0] || state.value[1])) + ? : null; + + return ( + + { + ({ value }) => { + const start = value[0]; + const end = value[1]; + return ( + + + ~ + + {clearIcon} + + + ); + } + } + + ); + } +} diff --git a/components/date-picker/createPicker.tsx b/components/date-picker/createPicker.tsx new file mode 100644 index 0000000000..0aea0d27fa --- /dev/null +++ b/components/date-picker/createPicker.tsx @@ -0,0 +1,148 @@ +import * as React from 'react'; +import MonthCalendar from 'rc-calendar/lib/MonthCalendar'; +import RcDatePicker from 'rc-calendar/lib/Picker'; +import GregorianCalendar from 'gregorian-calendar'; +import classNames from 'classnames'; +import assign from 'object-assign'; +import Icon from '../icon'; + +export default function createPicker(TheCalendar) { + // use class typescript error + const CalenderWrapper = React.createClass({ + + getInitialState() { + return { + value: this.props.parseDateFromValue(this.props.value || this.props.defaultValue), + }; + }, + + componentWillReceiveProps(nextProps) { + if ('value' in nextProps) { + this.setState({ + value: nextProps.parseDateFromValue(nextProps.value), + }); + } + }, + + clearSelection(e) { + e.preventDefault(); + e.stopPropagation(); + this.setState({ value: null }); + this.handleChange(null); + }, + + handleChange(value) { + const props = this.props; + if (!('value' in props)) { + this.setState({ value }); + } + const timeValue = value ? new Date(value.getTime()) : null; + props.onChange(timeValue, value ? props.getFormatter().format(value) : ''); + }, + + render() { + const props = this.props; + const locale = props.locale; + // 以下两行代码 + // 给没有初始值的日期选择框提供本地化信息 + // 否则会以周日开始排 + let defaultCalendarValue = new GregorianCalendar(locale); + defaultCalendarValue.setTime(Date.now()); + + const placeholder = ('placeholder' in props) + ? props.placeholder : locale.lang.placeholder; + + const disabledTime = props.showTime ? props.disabledTime : null; + + const calendarClassName = classNames({ + 'ant-calendar-time': props.showTime, + 'ant-calendar-month': MonthCalendar === TheCalendar, + }); + + // 需要选择时间时,点击 ok 时才触发 onChange + let pickerChangeHandler = { + onChange: this.handleChange, + }; + let calendarHandler = { + onOk: this.handleChange, + // fix https://github.com/ant-design/ant-design/issues/1902 + onSelect: (value, cause) => { + if (cause && cause.source === 'todayButton') { + this.handleChange(value); + } + }, + }; + if (props.showTime) { + pickerChangeHandler = {}; + } else { + calendarHandler = {}; + } + + const calendar = ( + + ); + + // default width for showTime + const pickerStyle = {}; + if (props.showTime) { + pickerStyle.width = 180; + } + + const clearIcon = (!props.disabled && this.state.value) ? + : null; + return ( + + + { + ({ value }) => { + return ( + + + {clearIcon} + + + ); + } + } + + + ); + }, + }); + + return CalenderWrapper; +} diff --git a/components/date-picker/demo/basic.md b/components/date-picker/demo/basic.md index 43561477b3..0746f61b44 100644 --- a/components/date-picker/demo/basic.md +++ b/components/date-picker/demo/basic.md @@ -1,16 +1,23 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- -- order: 0 +## zh-CN 最简单的用法。 ---- +## en-US + +The most basic usage. ````jsx import { DatePicker } from 'antd'; -function onChange(value) { - console.log(value); +function onChange(value, dateString) { + console.log(value, dateString); } ReactDOM.render(, mountNode); diff --git a/components/date-picker/demo/disabled-date.md b/components/date-picker/demo/disabled-date.md index 56d24bd692..23e8ccc0b8 100644 --- a/components/date-picker/demo/disabled-date.md +++ b/components/date-picker/demo/disabled-date.md @@ -1,12 +1,21 @@ -# 指定不可选择日期 +--- +order: 6 +title: + zh-CN: 指定不可选择日期 + en-US: Specify the date that cannot be selected +--- -- order: 6 +## zh-CN 设置 `disabledDate` 方法,来确定不可选时段。 如上例:不可选择今天之后的日期。 ---- +## en-US + +Specify unselectable period by `disabledDate`. + +As in the example above: you can't select a date later than today. ````jsx import { DatePicker } from 'antd'; diff --git a/components/date-picker/demo/disabled.md b/components/date-picker/demo/disabled.md index f3aec94a93..d6732ce8f5 100644 --- a/components/date-picker/demo/disabled.md +++ b/components/date-picker/demo/disabled.md @@ -1,10 +1,17 @@ -# 禁用 +--- +order: 5 +title: + zh-CN: 禁用 + en-US: Disabled +--- -- order: 5 +## zh-CN 选择框的不可用状态。 ---- +## en-US + +A disabled state of the `DatePicker`. ````jsx import { DatePicker } from 'antd'; diff --git a/components/date-picker/demo/formatter.md b/components/date-picker/demo/formatter.md index 62e6f7a3d4..b1a8634160 100644 --- a/components/date-picker/demo/formatter.md +++ b/components/date-picker/demo/formatter.md @@ -1,10 +1,17 @@ -# 日期格式 +--- +order: 1 +title: + zh-CN: 日期格式 + en-US: Date format +--- -- order: 1 +## zh-CN 使用 `format` 属性,可以自定义你需要的日期显示格式,如 `yyyy/MM/dd`。 ---- +## en-US + +By using `format`, you can customize the format(such as `yyyy/MM/dd`) the date is displayed in. ````jsx import { DatePicker } from 'antd'; diff --git a/components/date-picker/demo/locale.md b/components/date-picker/demo/locale.md index 3dc6764871..8e1ebcc655 100644 --- a/components/date-picker/demo/locale.md +++ b/components/date-picker/demo/locale.md @@ -1,31 +1,28 @@ -# 国际化 - -- order: 10 - -通过 `locale` 配置时区、语言等, 默认支持 en_US, zh_CN - --- +order: 10 +title: + zh-CN: 国际化 + en-US: Locale +--- + +## zh-CN + +通过 `locale` 配置时区、语言等, 默认支持 `en_US`,`zh_CN`。 + +## en-US + +Use locale to set the properties like time zone, language and etc. en_US, zh_CN are supported by default. + ````jsx import { DatePicker } from 'antd'; import enUS from 'antd/lib/date-picker/locale/en_US'; -import assign from 'object-assign'; -const App = React.createClass({ - getInitialState() { - return { - locale: assign({}, enUS, { - timezoneOffset: 0 * 60, - firstDayOfWeek: 0, - minimalDaysInFirstWeek: 1, - }) - }; - }, - render() { - return ; - } -}); +const customLocale = { + timezoneOffset: 0 * 60, + firstDayOfWeek: 0, + minimalDaysInFirstWeek: 1, +}; -ReactDOM.render(, mountNode); +ReactDOM.render(, mountNode); ```` - diff --git a/components/date-picker/demo/month-picker.md b/components/date-picker/demo/month-picker.md index 78cac64d4c..5c54f4c806 100644 --- a/components/date-picker/demo/month-picker.md +++ b/components/date-picker/demo/month-picker.md @@ -1,10 +1,17 @@ -# 月选择器 - -- order: 9 - -使用 `MonthPicker` 实现月选择器. - --- +order: 9 +title: + zh-CN: 月选择器 + en-US: MonthPicker +--- + +## zh-CN + +使用 `MonthPicker` 实现月选择器。 + +## en-US + +You can get a month selector by using `MonthPicker`. ````jsx import { DatePicker } from 'antd'; diff --git a/components/date-picker/demo/range.md b/components/date-picker/demo/range.md index 74c7b421e7..1b12eb2836 100644 --- a/components/date-picker/demo/range.md +++ b/components/date-picker/demo/range.md @@ -1,17 +1,27 @@ -# 日期范围二 +--- +order: 8 +title: + zh-CN: 日期范围二 + en-US: Date range, case 2 +--- -- order: 8 +## zh-CN 使用 `RangePicker` 实现日期范围选择有更好的交互体验。 ---- +## en-US + +By using `RangePicker` to specify a date range, you can achieve a better interactive experience. + + ````jsx import { DatePicker } from 'antd'; const RangePicker = DatePicker.RangePicker; -function onChange(value) { +function onChange(value, dateString) { console.log('From: ', value[0], ', to: ', value[1]); + console.log('From: ', dateString[0], ', to: ', dateString[1]); } ReactDOM.render(
diff --git a/components/date-picker/demo/size.md b/components/date-picker/demo/size.md index eb2a598525..132651517a 100644 --- a/components/date-picker/demo/size.md +++ b/components/date-picker/demo/size.md @@ -1,10 +1,18 @@ -# 三种大小 +--- +order: 1 +title: + zh-CN: 三种大小 + en-US: Three sizes +--- -- order: 1 +## zh-CN 三种大小的输入框,大的用在表单中,中的为默认。 ---- +## en-US + +The input box comes in three sizes. `large` is used in the form, while the medium size is the default. + ````jsx import { DatePicker } from 'antd'; diff --git a/components/date-picker/demo/start-end.md b/components/date-picker/demo/start-end.md index 6227d73de9..06d40251c8 100644 --- a/components/date-picker/demo/start-end.md +++ b/components/date-picker/demo/start-end.md @@ -1,10 +1,18 @@ -# 日期范围一 +--- +order: 7 +title: + zh-CN: 日期范围一 + en-US: Date range, case 1 +--- -- order: 7 +## zh-CN 可以设置 `disabledDate` 方法,来约束开始和结束日期。 ---- +## en-US + +You can use the `disabledDate` property to limit the start and end dates. + ````jsx import { DatePicker } from 'antd'; @@ -13,14 +21,15 @@ const DateRange = React.createClass({ getInitialState() { return { startValue: null, - endValue: null + endValue: null, + endOpen: false, }; }, disabledStartDate(startValue) { if (!startValue || !this.state.endValue) { return false; } - return startValue.getTime() >= this.state.endValue.getTime(); + return startValue.getTime() > this.state.endValue.getTime(); }, disabledEndDate(endValue) { if (!endValue || !this.state.startValue) { @@ -29,25 +38,49 @@ const DateRange = React.createClass({ return endValue.getTime() <= this.state.startValue.getTime(); }, onChange(field, value) { - console.log(field, 'change', value); this.setState({ [field]: value, }); }, + onStartChange(value) { + this.onChange('startValue', value); + }, + onEndChange(value) { + this.onChange('endValue', value); + }, + handleStartToggle({ open }) { + if (!open) { + this.setState({ endOpen: true }); + } + }, + handleEndToggle({ open }) { + this.setState({ endOpen: open }); + }, render() { return (
- - + + onChange={this.onEndChange} + open={this.state.endOpen} + toggleOpen={this.handleEndToggle} + />
); - } + }, }); ReactDOM.render( diff --git a/components/date-picker/demo/time.md b/components/date-picker/demo/time.md index 81de2d224c..b54fd56dd9 100644 --- a/components/date-picker/demo/time.md +++ b/components/date-picker/demo/time.md @@ -1,10 +1,17 @@ -# 日期时间选择 - -- order: 4 - -增加选择时间功能。不要修改时间的格式 `HH:mm:ss`。 - --- +order: 4 +title: + zh-CN: 日期时间选择 + en-US: To choose time +--- + +## zh-CN + +增加选择时间功能,当 `showTime` 为一个对象时,其属性会传递给内建的 `TimePicker`。 + +## en-US + +This property provide an additional time selection. When `showTime` is an Object, its properties will be passed on to `TimePicker`, witch is a built-in function. ````jsx import { DatePicker } from 'antd'; @@ -14,6 +21,6 @@ function onChange(value) { } ReactDOM.render( - + , mountNode); ```` diff --git a/components/date-picker/demo/with-time-picker.md b/components/date-picker/demo/with-time-picker.md index 0aab63a50e..d307d7261c 100644 --- a/components/date-picker/demo/with-time-picker.md +++ b/components/date-picker/demo/with-time-picker.md @@ -1,11 +1,18 @@ -# 日期时间选择二 - -- order: 4 -- hidden: true - -和 [时间选择框](/components/time-picker) 配合使用。 - --- +order: 4 +hidden: true +title: + zh-CN: 日期时间选择二 + en-US: To select a date, case 2 +--- + +## zh-CN + +和 时间选择框 配合使用。 + +## en-US + +Cooperate with `time-picker` ````jsx import { DatePicker, TimePicker } from 'antd'; @@ -36,14 +43,20 @@ const DateTimePicker = React.createClass({ this.props.onSelect(this.result); } }, + handleDateChange(value) { + this.handleChange('date', value); + }, + handleTimeChange(value) { + this.handleChange('time', value); + }, render() { return (
- - + +
); - } + }, }); function onSelect(value) { diff --git a/components/date-picker/index.en-US.md b/components/date-picker/index.en-US.md new file mode 100644 index 0000000000..6a5e4cc106 --- /dev/null +++ b/components/date-picker/index.en-US.md @@ -0,0 +1,74 @@ +--- +category: Components +type: Form Controls +english: DatePicker +--- + +To select/input a date. + +## When To Use + +By clicking the input box, you can select a date from a popup calendar. + +## API + +### DatePicker + +```html + +``` + +> Warning: `Datepicker` is renamed to `DatePicker` after `0.11`. + + +| Property | Description | Type | Default | +|--------------|----------------|----------|--------------| +| value | to set date | String/Date | - | +| defaultValue | to set default date | String/Date | - | +| format | to set the date format, refer to [GregorianCalendarFormat](https://github.com/yiminghe/gregorian-calendar-format) | String | "yyyy-MM-dd" | +| disabledDate | to specify the date that cannot be selected | function | - | +| onChange | a callback function, can be executed when the selected time is changing | function(date, dateString) | - | +| disabled | determine whether the DatePicker is disabled | Boolean | false | +| style | to customize the style of the input box | Object | {} | +| popupStyle | to customize the style of the popup calendar | Object | {} | +| size | determine the size of the input box, the height of `large` and `small`, are 32px and 22px respectively, while default size is 28px | String | - | +| locale | localization configuration | Object | [default](https://github.com/ant-design/ant-design/issues/424) | +| onOk | a callback function, can be executed when OK-button is clicked | function(Date value) | - | +| toggleOpen | a callback function, can be executed whether the popup calendar is popped up or closed | function(status) | - | +| getCalendarContainer | to set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - | +| showTime | to provide an additional time selection | Object/Boolean | [TimePicker Options](http://ant.design/components/time-picker/#api) | + +### MonthPicker + +| Property | Description | Type | Default | +|--------------|----------------|----------|--------------| +| value | to set date | String/Date | - | +| defaultValue | to set default date | String/Date | - | +| format | to set the date format, refer to [GregorianCalendarFormat](https://github.com/yiminghe/gregorian-calendar-format) | String | "yyyy-MM" | +| disabledDate | to specify the date that cannot be selected | function | - | +| onChange | a callback function, can be executed when the selected time is changing | function(Date value) | - | +| disabled | determine whether the MonthPicker is disabled | Boolean | false | +| style | to customize the style of the input box | Object | {} | +| popupStyle | to customize the style of the popup calendar | Object | {} | +| size | determine the size of the input box, the height of `large` and `small`, are 32px and 22px respectively, while default size is 28px | String | - | +| locale | localization configuration | Object | [default](https://github.com/ant-design/ant-design/issues/424) | +| getCalendarContainer | to set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - | + +### RangePicker + +| Property | Description | Type | Default | +|--------------|----------------|----------|--------------| +| value | to set date | [String/Date, String/Date] | - | +| defaultValue | to set default date | [String/Date, String/Date] | - | +| format | to set the date format | String | "yyyy-MM-dd HH:mm:ss" | +| onChange | a callback function, can be executed when the selected time is changing | function(date[], dateString[]) | - | +| showTime | to provide an additional time selection | Object/Boolean | [TimePicker Options](http://ant.design/components/time-picker/#api) | + +The following properties are the same with `DatePicker`: `disabled` `style` `popupStyle` `size` `locale` `showTime` `onOk` `getCalendarContainer` + + + diff --git a/components/date-picker/index.jsx b/components/date-picker/index.jsx deleted file mode 100644 index 85670591ec..0000000000 --- a/components/date-picker/index.jsx +++ /dev/null @@ -1,174 +0,0 @@ -import React from 'react'; -import Calendar from 'rc-calendar'; -import MonthCalendar from 'rc-calendar/lib/MonthCalendar'; -import DatePicker from 'rc-calendar/lib/Picker'; -import GregorianCalendar from 'gregorian-calendar'; -import CalendarLocale from 'rc-calendar/lib/locale/zh_CN'; -import AntRangePicker from './RangePicker'; -import PickerMixin from './PickerMixin'; -import TimePicker from 'rc-time-picker'; -import classNames from 'classnames'; - -function createPicker(TheCalendar, defaultFormat) { - return React.createClass({ - getDefaultProps() { - return { - format: defaultFormat || 'yyyy-MM-dd', - transitionName: 'slide-up', - popupStyle: {}, - onChange() { - }, - onOk() { - }, - locale: {}, - align: { - offset: [0, -9], - }, - open: false, - }; - }, - getInitialState() { - return { - value: this.parseDateFromValue(this.props.value || this.props.defaultValue) - }; - }, - mixins: [PickerMixin], - componentWillReceiveProps(nextProps) { - if ('value' in nextProps) { - this.setState({ - value: this.parseDateFromValue(nextProps.value) - }); - } - }, - handleChange(value) { - if (!('value' in this.props)) { - this.setState({ value }); - } - const timeValue = value ? new Date(value.getTime()) : null; - this.props.onChange(timeValue); - }, - render() { - const locale = this.getLocale(); - // 以下两行代码 - // 给没有初始值的日期选择框提供本地化信息 - // 否则会以周日开始排 - let defaultCalendarValue = new GregorianCalendar(locale); - defaultCalendarValue.setTime(Date.now()); - - const placeholder = ('placeholder' in this.props) - ? this.props.placeholder : locale.lang.placeholder; - - const timePicker = this.props.showTime ? () - : null; - - const disabledTime = this.props.showTime ? this.props.disabledTime : null; - - const calendarClassName = classNames({ - ['ant-calendar-time']: this.props.showTime, - ['ant-calendar-month']: MonthCalendar === TheCalendar, - }); - - let pickerChangeHandler = { - onChange: this.handleChange, - }; - - let calendarHandler = { - onOk: this.handleChange, - }; - - if (this.props.showTime) { - pickerChangeHandler.onChange = (value) => { - // Click clear button - if (value === null) { - this.handleChange(value); - } - }; - } else { - calendarHandler = {}; - } - - const calendar = ( - - ); - - let sizeClass = ''; - if (this.props.size === 'large') { - sizeClass = ' ant-input-lg'; - } else if (this.props.size === 'small') { - sizeClass = ' ant-input-sm'; - } - - let pickerClass = 'ant-calendar-picker'; - if (this.state.open) { - pickerClass += ' ant-calendar-picker-open'; - } - return ( - - - { - ({ value }) => { - return ( - - - - - ); - } - } - - - ); - } - }); -} - -const AntDatePicker = createPicker(Calendar); -const AntMonthPicker = createPicker(MonthCalendar, 'yyyy-MM'); - -const AntCalendar = React.createClass({ - getDefaultProps() { - return { - locale: CalendarLocale, - prefixCls: 'ant-calendar', - }; - }, - render() { - return ; - } -}); - -AntDatePicker.Calendar = AntCalendar; -AntDatePicker.RangePicker = AntRangePicker; -AntDatePicker.MonthPicker = AntMonthPicker; - -export default AntDatePicker; diff --git a/components/date-picker/index.tsx b/components/date-picker/index.tsx new file mode 100644 index 0000000000..9c33a6efb4 --- /dev/null +++ b/components/date-picker/index.tsx @@ -0,0 +1,15 @@ +import RcCalendar from 'rc-calendar'; +import MonthCalendar from 'rc-calendar/lib/MonthCalendar'; +import createPicker from './createPicker'; +import wrapPicker from './wrapPicker'; +import RangePicker from './RangePicker'; +import Calendar from './Calendar'; + +const DatePicker = wrapPicker(createPicker(RcCalendar)); +const MonthPicker = wrapPicker(createPicker(MonthCalendar), 'yyyy-MM'); + +DatePicker.Calendar = Calendar; +DatePicker.RangePicker = wrapPicker(RangePicker); +DatePicker.MonthPicker = MonthPicker; + +export default DatePicker; diff --git a/components/date-picker/index.md b/components/date-picker/index.zh-CN.md similarity index 56% rename from components/date-picker/index.md rename to components/date-picker/index.zh-CN.md index 9d9ad02a3f..4582d2e3e1 100644 --- a/components/date-picker/index.md +++ b/components/date-picker/index.zh-CN.md @@ -1,9 +1,8 @@ -# DatePicker - -- category: Components -- chinese: 日期选择框 -- type: 表单 - +--- +category: Components +chinese: 日期选择框 +type: Form Controls +english: DatePicker --- 输入或选择日期的控件。 @@ -29,14 +28,30 @@ | defaultValue | 默认日期 | string or Date | 无 | | format | 展示的日期格式,配置参考 [GregorianCalendarFormat](https://github.com/yiminghe/gregorian-calendar-format) | string | "yyyy-MM-dd" | | disabledDate | 不可选择的日期 | function | 无 | +| onChange | 时间发生变化的回调 | function(date, dateString) | 无 | +| disabled | 禁用 | bool | false | +| style | 自定义输入框样式 | object | {} | +| popupStyle | 格外的弹出日历样式 | object | {} | +| size | 输入框大小,`large` 高度为 32px,`small` 为 22px,默认是 28px | string | 无 | +| locale | 国际化配置 | object | [默认配置](https://github.com/ant-design/ant-design/issues/424) | +| toggleOpen | 弹出日历和关闭日历的回调 | function(status) | 无 | +| getCalendarContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | 无 | +| showTime | 增加时间选择功能 | Object or Boolean | [TimePicker Options](http://ant.design/components/time-picker/#api) | + +### MonthPicker + +| 参数 | 说明 | 类型 | 默认值 | +|--------------|----------------|----------|--------------| +| value | 日期 | string or Date | 无 | +| defaultValue | 默认日期 | string or Date | 无 | +| format | 展示的日期格式,配置参考 [GregorianCalendarFormat](https://github.com/yiminghe/gregorian-calendar-format) | string | "yyyy-MM" | +| disabledDate | 不可选择的日期 | function | 无 | | onChange | 时间发生变化的回调,发生在用户选择时间时 | function(Date value) | 无 | | disabled | 禁用 | bool | false | | style | 自定义输入框样式 | object | {} | | popupStyle | 格外的弹出日历样式 | object | {} | | size | 输入框大小,`large` 高度为 32px,`small` 为 22px,默认是 28px | string | 无 | | locale | 国际化配置 | object | [默认配置](https://github.com/ant-design/ant-design/issues/424) | -| showTime | 增加时间选择功能 | bool | false | -| onOk | 点击确定按钮的回调 | function(Date value) | 无 | | getCalendarContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | 无 | ### RangePicker @@ -46,7 +61,8 @@ | value | 日期 | [string/Date, string/Date] | 无 | | defaultValue | 默认日期 | [string/Date, string/Date] | 无 | | format | 展示的日期格式 | string | "yyyy-MM-dd HH:mm:ss" | -| onChange | 时间发生变化的回调,发生在用户选择时间时 | function([Date start, Date end]) | 无 | +| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date[], dateString[]) | 无 | +| showTime | 增加时间选择功能 | Object or Boolean | [TimePicker Options](http://ant.design/components/time-picker/#api) | `disabled` `style` `popupStyle` `size` `locale` `showTime` `onOk` `getCalendarContainer` 属性与 DatePicker 的一致。 diff --git a/components/date-picker/locale/en_US.js b/components/date-picker/locale/en_US.tsx similarity index 53% rename from components/date-picker/locale/en_US.js rename to components/date-picker/locale/en_US.tsx index aff512e58b..345128852d 100644 --- a/components/date-picker/locale/en_US.js +++ b/components/date-picker/locale/en_US.tsx @@ -1,14 +1,16 @@ -import objectAssign from 'object-assign'; import GregorianCalendarLocale from 'gregorian-calendar/lib/locale/en_US'; import CalendarLocale from 'rc-calendar/lib/locale/en_US'; - +import TimePickerLocale from '../../time-picker/locale/en_US'; +import assign from 'object-assign'; // 统一合并为完整的 Locale -let locale = objectAssign({}, GregorianCalendarLocale); -locale.lang = objectAssign({ +const locale = assign({}, GregorianCalendarLocale); +locale.lang = assign({ placeholder: 'Select date', - timePlaceholder: 'Select time', + rangePlaceholder: ['Start date', 'End date'], }, CalendarLocale); +locale.timePickerLocale = assign({}, TimePickerLocale); + // All settings at: // https://github.com/ant-design/ant-design/issues/424 diff --git a/components/date-picker/locale/ru_RU.tsx b/components/date-picker/locale/ru_RU.tsx new file mode 100644 index 0000000000..80a46415fc --- /dev/null +++ b/components/date-picker/locale/ru_RU.tsx @@ -0,0 +1,20 @@ +/** + * Created by Andrey Gayvoronsky on 13/04/16. + */ + +import GregorianCalendarLocale from 'gregorian-calendar/lib/locale/ru_RU'; +import CalendarLocale from 'rc-calendar/lib/locale/ru_RU'; +import TimePickerLocale from '../../time-picker/locale/ru_RU'; +import assign from 'object-assign'; +const locale = assign({}, GregorianCalendarLocale); +locale.lang = assign({ + placeholder: 'Выберите дату', + rangePlaceholder: ['Начальная дата', 'Конечная дата'], +}, CalendarLocale); + +locale.timePickerLocale = assign({}, TimePickerLocale); + +// All settings at: +// https://github.com/ant-design/ant-design/issues/424 + +export default locale; diff --git a/components/date-picker/locale/zh_CN.js b/components/date-picker/locale/zh_CN.tsx similarity index 58% rename from components/date-picker/locale/zh_CN.js rename to components/date-picker/locale/zh_CN.tsx index b6a9177c70..350de8ce06 100644 --- a/components/date-picker/locale/zh_CN.js +++ b/components/date-picker/locale/zh_CN.tsx @@ -1,14 +1,16 @@ -import objectAssign from 'object-assign'; import GregorianCalendarLocale from 'gregorian-calendar/lib/locale/zh_CN'; import CalendarLocale from 'rc-calendar/lib/locale/zh_CN'; - +import TimePickerLocale from '../../time-picker/locale/zh_CN'; +import assign from 'object-assign'; // 统一合并为完整的 Locale -let locale = objectAssign({}, GregorianCalendarLocale); -locale.lang = objectAssign({ +const locale = assign({}, GregorianCalendarLocale); +locale.lang = assign({ placeholder: '请选择日期', - timePlaceholder: '请选择时间', + rangePlaceholder: ['开始日期', '结束日期'], }, CalendarLocale); +locale.timePickerLocale = assign({}, TimePickerLocale); + // should add whitespace between char in Button locale.lang.ok = '确 定'; diff --git a/style/components/datepicker/Calendar.less b/components/date-picker/style/Calendar.less similarity index 94% rename from style/components/datepicker/Calendar.less rename to components/date-picker/style/Calendar.less index 78526cc48a..61864d2372 100644 --- a/style/components/datepicker/Calendar.less +++ b/components/date-picker/style/Calendar.less @@ -38,7 +38,7 @@ position: absolute; top: 0; color: #999; - font-family: Arial,"Hiragino Sans GB","Microsoft Yahei","Microsoft Sans Serif",sans-serif; + font-family: Arial, "Hiragino Sans GB", "Microsoft Yahei", "Microsoft Sans Serif", sans-serif; padding: 0 5px; font-size: 16px; display: inline-block; @@ -106,7 +106,7 @@ .calendarPanelHeader(@calendar-prefix-cls); } - &-calendar-body { + &-body { padding: 4px 8px; } @@ -163,6 +163,10 @@ text-align: center; transition: background 0.3s ease; + &-panel { + position: relative; + } + &:hover { background: tint(@primary-color, 90%); cursor: pointer; @@ -246,6 +250,7 @@ } .@{calendar-prefix-cls}-clear-btn { + display: none; position: absolute; right: 5px; text-indent: -76px; @@ -259,8 +264,7 @@ } .@{calendar-prefix-cls}-clear-btn:after { - content: "\e631"; - font-family: "anticon"; + .iconfont-font("\e631"); font-size: 12px; color: #ccc; display: inline-block; @@ -285,6 +289,9 @@ &-disabled { .button-color(@btn-disable-color; @btn-disable-bg; @btn-disable-border); cursor: not-allowed; + &:hover { + .button-color(@btn-disable-color; @btn-disable-bg; @btn-disable-border); + } } } } diff --git a/style/components/datepicker/DecadePanel.less b/components/date-picker/style/DecadePanel.less similarity index 100% rename from style/components/datepicker/DecadePanel.less rename to components/date-picker/style/DecadePanel.less diff --git a/style/components/datepicker/MonthPanel.less b/components/date-picker/style/MonthPanel.less similarity index 100% rename from style/components/datepicker/MonthPanel.less rename to components/date-picker/style/MonthPanel.less diff --git a/style/components/datepicker/MonthPicker.less b/components/date-picker/style/MonthPicker.less similarity index 100% rename from style/components/datepicker/MonthPicker.less rename to components/date-picker/style/MonthPicker.less diff --git a/style/components/datepicker/Picker.less b/components/date-picker/style/Picker.less similarity index 77% rename from style/components/datepicker/Picker.less rename to components/date-picker/style/Picker.less index 0e539d613b..d2c1c05ef4 100644 --- a/style/components/datepicker/Picker.less +++ b/components/date-picker/style/Picker.less @@ -1,6 +1,6 @@ .@{calendar-prefix-cls}-picker-container { position: absolute; - z-index: 1070; + z-index: @zindex-picker; &.slide-up-enter.slide-up-enter-active&-placement-topLeft, &.slide-up-enter.slide-up-enter-active&-placement-topRight, @@ -37,16 +37,34 @@ > input { outline: none; } - - &-open > span:first-child { - transition: opacity 0.3s ease; + &-clear { opacity: 0; + z-index: -1; + position: absolute; + right: 7px; + background: #fff; + top: 50%; + font-size: 12px; + color: #ccc; + width: 14px; + height: 14px; + margin-top: -7px; + line-height: 14px; + cursor: pointer; + transition: color 0.3s ease, opacity 0.15s ease; + &:hover { + color: #999; + } } + &:hover &-clear { + opacity: 1; + z-index: 1; + } &-icon { position: absolute; user-select: none; - .transition(all .3s @ease-in-out); + transition: all .3s @ease-in-out; width: 12px; height: 12px; line-height: 12px; diff --git a/style/components/datepicker/RangePicker.less b/components/date-picker/style/RangePicker.less similarity index 51% rename from style/components/datepicker/RangePicker.less rename to components/date-picker/style/RangePicker.less index e3ae488238..8b66219100 100644 --- a/style/components/datepicker/RangePicker.less +++ b/components/date-picker/style/RangePicker.less @@ -26,16 +26,37 @@ width: 470px; overflow: hidden; + .@{calendar-prefix-cls}-date-panel { + &::after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; + } + } &-part { width: 50%; + position: relative; } &-left { float: left; + .@{calendar-prefix-cls} { + &-time-picker-inner { + border-right: 1px solid @border-color-split; + } + } } &-right { float: right; + .@{calendar-prefix-cls} { + &-time-picker-inner { + margin-left: 21px; + border-left: 1px solid @border-color-split; + } + } } &-middle { @@ -62,6 +83,7 @@ } .@{calendar-prefix-cls}-input-wrap { + position: relative; height: @input-box-height; } .@{calendar-prefix-cls}-input { @@ -69,13 +91,18 @@ border-radius: @border-radius-sm; } .@{calendar-prefix-cls}-input, - .@{css-prefix}time-picker-input { + .@{timepicker-prefix-cls}-input { .input; border-radius: @border-radius-sm; height: @input-height-sm; - width: 96px; + border: 0; + box-shadow: none; + + &:focus { + box-shadow: none; + } } - .@{css-prefix}time-picker-icon { + .@{timepicker-prefix-cls}-icon { display: none; } @@ -109,24 +136,89 @@ .@{calendar-prefix-cls}-in-range-cell { border-radius: 0; - > span { + position: relative; + > div { + position: relative; + z-index: 1; + } + &:before { + content: ''; + display: block; background: tint(@primary-color, 90%); - width: 100%; border-radius: 0; border: 0; + position: absolute; + top: 4px; + bottom: 4px; + left: 0; + right: 0; } } &-bottom { text-align: right; } - .@{calendar-prefix-cls}-ok-btn { - position: static; - height: 22px; - margin: 8px; + + .@{calendar-prefix-cls} { + &-header { + border-bottom: 0; + } + &-body { + border-top: 1px solid @border-color-split; + } } - .@{calendar-prefix-cls}-today-btn { - margin: 8px 12px; - height: 22px; + + &.@{calendar-prefix-cls}-time { + .@{timepicker-prefix-cls} { + height: 207px; + top: 68px; + z-index: 2; // in order to cover .ant-calendar-range .ant-calendar-in-range-cell > div (z-index: 1) + &-panel { + height: 241px; + margin-top: -34px; + } + + &-inner { + padding-top: 34px; + height: 241px; + background: none; + } + + &-combobox { + display: inline-block; + background-color: white; + border-top: 1px solid @border-color-split; + } + &-select { + width: 71px; + + ul { + max-height: 206px; + } + } + } + .@{calendar-prefix-cls}-footer-btn { + padding-right: 12px; + display: block; + &::after { + content: 'x'; + height: 0; + font-size: 0; + overflow: hidden; + clear: both; + } + } + .@{calendar-prefix-cls}-ok-btn { + position: static; + height: 22px; + } + .@{calendar-prefix-cls}-footer .@{calendar-prefix-cls}-time-picker-btn { + margin-right: 12px; + } + .@{calendar-prefix-cls}-today-btn { + margin: 8px 12px; + height: 22px; + line-height: 22px; + } } } diff --git a/components/date-picker/style/TimePicker.less b/components/date-picker/style/TimePicker.less new file mode 100644 index 0000000000..e4680e1b78 --- /dev/null +++ b/components/date-picker/style/TimePicker.less @@ -0,0 +1,262 @@ +.@{timepicker-prefix-cls} { + position: absolute; + width: 100%; + top: 34px; + background-color: white; + height: 206px; + + &-panel { + z-index: @zindex-picker; + position: absolute; + width: 100%; + } + + &-inner { + display: inline-block; + position: relative; + outline: none; + list-style: none; + font-size: 12px; + text-align: left; + background-color: #fff; + background-clip: padding-box; + line-height: 1.5; + overflow: hidden; + } + + &-input { + margin: 0; + padding: 0; + border: 0; + width: 100%; + cursor: auto; + line-height: 1.5; + outline: 0; + + &-wrap { + display: none; + box-sizing: border-box; + position: relative; + padding: 6px; + border-bottom: 1px solid @border-color-split; + } + + &-invalid { + border-color: red; + } + } + + &-clear-btn { + position: absolute; + right: 5px; + cursor: pointer; + overflow: hidden; + width: 20px; + height: 20px; + text-align: center; + line-height: 20px; + top: 5px; + margin: 0; + } + + &-clear-btn:after { + content: "\e631"; + font-family: "anticon"; + font-size: 12px; + color: #ccc; + display: inline-block; + line-height: 1; + width: 20px; + transition: color 0.3s ease; + } + + &-clear-btn:hover:after { + color: #999; + } + + &-narrow &-input-wrap { + max-width: 111px; + } + + &-select { + float: left; + font-size: 12px; + border: 1px solid @border-color-split; + border-width: 0 1px; + margin-left: -1px; + box-sizing: border-box; + width: 77px; + overflow: hidden; + position: relative; // Fix chrome weird render bug + + &:hover { + overflow-y: auto; + } + + &:first-child { + border-left: 0; + margin-left: 0; + } + + &:last-child { + border-right: 0; + } + + ul { + list-style: none; + box-sizing: border-box; + margin: 0; + padding: 0; + width: 100%; + max-height: 206px; + } + + li { + text-align: center; + list-style: none; + box-sizing: content-box; + margin: 0; + width: 100%; + height: 24px; + line-height: 24px; + cursor: pointer; + user-select: none; + transition: background 0.3s ease; + } + + li:last-child:after { + content: ''; + height: 182px; + display: block; + } + + li:hover { + background: tint(@primary-color, 90%); + } + + li&-option-selected { + background: @background-color-base; + font-weight: bold; + } + + li&-option-disabled { + color: @btn-disable-color; + &:hover { + background: transparent; + cursor: @cursor-disabled; + } + } + } + + &.slide-up-enter.slide-up-enter-active&-placement-topLeft, + &.slide-up-enter.slide-up-enter-active&-placement-topRight, + &.slide-up-appear.slide-up-appear-active&-placement-topLeft, + &.slide-up-appear.slide-up-appear-active&-placement-topRight { + animation-name: antSlideDownIn; + } + + &.slide-up-enter.slide-up-enter-active&-placement-bottomLeft, + &.slide-up-enter.slide-up-enter-active&-placement-bottomRight, + &.slide-up-appear.slide-up-appear-active&-placement-bottomLeft, + &.slide-up-appear.slide-up-appear-active&-placement-bottomRight { + animation-name: antSlideUpIn; + } + + &.slide-up-leave.slide-up-leave-active&-placement-topLeft, + &.slide-up-leave.slide-up-leave-active&-placement-topRight { + animation-name: antSlideDownOut; + } + + &.slide-up-leave.slide-up-leave-active&-placement-bottomLeft, + &.slide-up-leave.slide-up-leave-active&-placement-bottomRight { + animation-name: antSlideUpOut; + } +} + +.@{timepicker-prefix-cls} { + display: inline-block; + outline: none; + font-size: @font-size-base; + + &-input { + .input; + width: 100px; + } + + &-large &-input { + .input-lg; + } + + &-small &-input { + .input-sm; + } + + &-open { + opacity: 0; + } + + &-icon { + position: absolute; + user-select: none; + transition: all .3s @ease-in-out; + width: 12px; + height: 12px; + line-height: 12px; + right: 8px; + color: #999; + top: 50%; + margin-top: -6px; + &:after { + content: "\e643"; + font-family: "anticon"; + font-size: 12px; + color: #999; + display: inline-block; + line-height: 1; + vertical-align: bottom; + } + } +} + + +.@{calendar-prefix-cls} { + &-time { + .@{calendar-prefix-cls}-day-select { + padding: 0 2px; + font-weight: bold; + display: inline-block; + color: #666; + line-height: 34px; + } + + .@{calendar-prefix-cls}-footer { + border-top: 1px solid @border-color-split; + padding: 10px 0; + text-align: right; + position: relative; + height: auto; + line-height: auto; + + &-btn { + line-height: 1.5; + text-align: right; + } + + .@{calendar-prefix-cls}-today-btn { + float: left; + margin: 0; + padding-left: 12px; + } + + .@{calendar-prefix-cls}-time-picker-btn { + display: inline-block; + text-align: center; + margin-right: 60px; + + &-disabled { + color: #ccc; + } + } + } + } +} diff --git a/style/components/datepicker/YearPanel.less b/components/date-picker/style/YearPanel.less similarity index 100% rename from style/components/datepicker/YearPanel.less rename to components/date-picker/style/YearPanel.less diff --git a/components/date-picker/style/index.less b/components/date-picker/style/index.less new file mode 100644 index 0000000000..bfe71a54bd --- /dev/null +++ b/components/date-picker/style/index.less @@ -0,0 +1,16 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; +@import "../../input/style/mixin"; +@import "../../button/style/mixin"; + +@calendar-prefix-cls: ant-calendar; +@timepicker-prefix-cls: ant-calendar-time-picker; + +@import "Picker"; +@import "Calendar"; +@import "RangePicker"; +@import "TimePicker"; +@import "MonthPanel"; +@import "YearPanel"; +@import "DecadePanel"; +@import "MonthPicker"; diff --git a/components/date-picker/style/index.tsx b/components/date-picker/style/index.tsx new file mode 100644 index 0000000000..6d6ebe600f --- /dev/null +++ b/components/date-picker/style/index.tsx @@ -0,0 +1,6 @@ +import '../../style/index.less'; +import './index.less'; + +// style dependencies +import '../../input/style'; +import '../../time-picker/style'; diff --git a/components/date-picker/wrapPicker.tsx b/components/date-picker/wrapPicker.tsx new file mode 100644 index 0000000000..5eece135f8 --- /dev/null +++ b/components/date-picker/wrapPicker.tsx @@ -0,0 +1,116 @@ +import { PropTypes } from 'react'; +import * as React from 'react'; +import TimePickerPanel from 'rc-time-picker/lib/module/Panel'; +import DateTimeFormat from 'gregorian-calendar-format'; +import GregorianCalendar from 'gregorian-calendar'; +import classNames from 'classnames'; +import defaultLocale from './locale/zh_CN'; +import assign from 'object-assign'; + +export default function wrapPicker(Picker, defaultFormat) { + const PickerWrapper = React.createClass({ + getDefaultProps() { + return { + format: defaultFormat || 'yyyy-MM-dd', + transitionName: 'slide-up', + popupStyle: {}, + onChange() { + }, + onOk() { + }, + toggleOpen() { + }, + locale: {}, + align: { + offset: [0, -9], + }, + }; + }, + + contextTypes: { + antLocale: PropTypes.object, + }, + + getLocale() { + const props = this.props; + let locale = defaultLocale; + const context = this.context; + if (context.antLocale && context.antLocale.DatePicker) { + locale = context.antLocale.DatePicker; + } + // 统一合并为完整的 Locale + const result = assign({}, locale, props.locale); + result.lang = assign({}, locale.lang, props.locale.lang); + return result; + }, + + getFormatter() { + const format = this.props.format; + const formatter = new DateTimeFormat(format, this.getLocale().lang.format); + return formatter; + }, + + parseDateFromValue(value) { + if (value) { + if (typeof value === 'string') { + return this.getFormatter().parse(value, {locale: this.getLocale()}); + } else if (value instanceof Date) { + let date = new GregorianCalendar(this.getLocale()); + date.setTime(+value); + return date; + } + } + return value; + }, + + toggleOpen ({open}) { + this.props.toggleOpen({open}); + }, + + render() { + const props = this.props; + const pickerClass = classNames({ + 'ant-calendar-picker': true, + }); + const pickerInputClass = classNames({ + 'ant-calendar-range-picker': true, + 'ant-input': true, + 'ant-input-lg': props.size === 'large', + 'ant-input-sm': props.size === 'small', + }); + + const locale = this.getLocale(); + + const timeFormat = props.showTime && props.showTime.format; + const rcTimePickerProps = { + formatter: new DateTimeFormat(timeFormat || 'HH:mm:ss', locale.timePickerLocale.format), + showSecond: timeFormat && timeFormat.indexOf('ss') >= 0, + showHour: timeFormat && timeFormat.indexOf('HH') >= 0, + }; + const timePicker = props.showTime ? ( + + ) : null; + + return ( + + ); + }, + }); + return PickerWrapper; +} diff --git a/components/dropdown/demo/basic.md b/components/dropdown/demo/basic.md index afb6bce6d9..2c7e45a9d0 100644 --- a/components/dropdown/demo/basic.md +++ b/components/dropdown/demo/basic.md @@ -1,10 +1,17 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- -- order: 0 +## zh-CN 最简单的下拉菜单。 ---- +## en-US + +The most basic dropdown menu. ````jsx import { Menu, Dropdown, Icon } from 'antd'; diff --git a/components/dropdown/demo/dropdown-button.md b/components/dropdown/demo/dropdown-button.md index c4fddb9d7d..236c138896 100644 --- a/components/dropdown/demo/dropdown-button.md +++ b/components/dropdown/demo/dropdown-button.md @@ -1,17 +1,24 @@ -# 带下拉框的按钮 +--- +order: 4 +title: + zh-CN: 带下拉框的按钮 + en-US: Button with dropdown menu +--- -- order: 4 +## zh-CN 左边是按钮,右边是额外的相关功能菜单。 ---- +## en-US + +A button is on the left, and a related functional menu is on the right. ````jsx import { Menu, Dropdown } from 'antd'; const DropdownButton = Dropdown.Button; -function handleButtonClick() { - console.log('click button'); +function handleButtonClick(e) { + console.log('click left button', e); } function handleMenuClick(e) { diff --git a/components/dropdown/demo/event.md b/components/dropdown/demo/event.md index 15e189cac8..34f2072f69 100644 --- a/components/dropdown/demo/event.md +++ b/components/dropdown/demo/event.md @@ -1,10 +1,17 @@ -# 触发事件 +--- +order: 3 +title: + zh-CN: 触发事件 + en-US: Click event +--- -- order: 3 +## zh-CN 点击菜单项后会触发事件,用户可以通过相应的菜单项 key 进行不同的操作。 ---- +## en-US + +An event will be triggered when you click menu items, in which you can make different operations according to item's key. ````jsx import { Menu, Dropdown, Icon } from 'antd'; diff --git a/components/dropdown/demo/item.md b/components/dropdown/demo/item.md index 5e6d379239..4668428509 100644 --- a/components/dropdown/demo/item.md +++ b/components/dropdown/demo/item.md @@ -1,10 +1,17 @@ -# 其他元素 +--- +order: 1 +title: + zh-CN: 其他元素 + en-US: Other elements +--- -- order: 1 +## zh-CN 分割线和不可用菜单项。 ---- +## en-US + +Divider and disabled menu item. ````jsx import { Menu, Dropdown, Icon } from 'antd'; diff --git a/components/dropdown/demo/overlay-visible.md b/components/dropdown/demo/overlay-visible.md new file mode 100644 index 0000000000..12bf9ca7d0 --- /dev/null +++ b/components/dropdown/demo/overlay-visible.md @@ -0,0 +1,55 @@ +--- +order: 6 +title: + zh-CN: 菜单隐藏方式 + en-US: The way of hiding menu. +--- + +## zh-CN + +默认是点击关闭菜单,可以关闭此功能。 + +## en-US + +The default is to close the menu when you click on menu items, this feature can be turned off. + +````jsx +import { Menu, Dropdown, Icon } from 'antd'; + +const OverlayVisible = React.createClass({ + getInitialState() { + return { + visible: false, + }; + }, + handleMenuClick(e) { + if (e.key === '3') { + this.setState({ visible: false }); + } + }, + handleVisibleChange(flag) { + this.setState({ visible: flag }); + }, + render() { + const menu = ( + + 点我不会关闭菜单 + 点我还是不会关闭菜单 + 点我才会关闭菜单 + + ); + return ( + + + 鼠标移入 + + + ); + }, +}); + +ReactDOM.render(, mountNode); +```` diff --git a/components/dropdown/demo/sub-menu.md b/components/dropdown/demo/sub-menu.md index 528360b8fb..f7b4115c77 100644 --- a/components/dropdown/demo/sub-menu.md +++ b/components/dropdown/demo/sub-menu.md @@ -1,10 +1,17 @@ -# 多级菜单 +--- +order: 5 +title: + zh-CN: 多级菜单 + en-US: Cascading menu +--- -- order: 5 +## zh-CN 传入的菜单里有多个层级。 ---- +## en-US + +The menu has multiple levels. ````jsx import { Menu, Dropdown, Icon } from 'antd'; diff --git a/components/dropdown/demo/trigger.md b/components/dropdown/demo/trigger.md index 2a7e8cf04f..4fbc7d41b6 100644 --- a/components/dropdown/demo/trigger.md +++ b/components/dropdown/demo/trigger.md @@ -1,10 +1,17 @@ -# 触发方式 +--- +order: 2 +title: + zh-CN: 触发方式 + en-US: Trigger mode +--- -- order: 2 +## zh-CN 默认是移入触发菜单,可以点击触发。 ---- +## en-US + +The default trigger mode is `hover`, you can change it to `click`. ````jsx import { Menu, Dropdown, Icon } from 'antd'; diff --git a/components/dropdown/dropdown-button.jsx b/components/dropdown/dropdown-button.jsx deleted file mode 100644 index 675f97c5c9..0000000000 --- a/components/dropdown/dropdown-button.jsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import Button from '../button'; -import Icon from '../icon'; -import Dropdown from './dropdown'; -const ButtonGroup = Button.Group; -import classNames from 'classnames'; - -export default React.createClass({ - getDefaultProps() { - return { - align: { - points: ['tr', 'br'], - overlay: { - adjustX: 1, - adjustY: 1, - }, - offset: [0, 4], - targetOffset: [0, 0], - }, - type: 'default', - }; - }, - render() { - const { type, overlay, trigger, align, children, className, ...restProps } = this.props; - const cls = classNames({ - 'ant-dropdown-button': true, - className: !!className, - }); - return ( - - - - - - - ); - } -}); diff --git a/components/dropdown/dropdown-button.tsx b/components/dropdown/dropdown-button.tsx new file mode 100644 index 0000000000..acdacad89b --- /dev/null +++ b/components/dropdown/dropdown-button.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import Button from '../button'; +import Icon from '../icon'; +import Dropdown from './dropdown'; +const ButtonGroup = Button.Group; +import classNames from 'classnames'; +import splitObject from '../_util/splitObject'; +export default class DropdownButton extends React.Component { + static defaultProps = { + align: { + points: ['tr', 'br'], + overlay: { + adjustX: 1, + adjustY: 1, + }, + offset: [0, 4], + targetOffset: [0, 0], + }, + type: 'default', + }; + + render() { + const [{ type, overlay, trigger, align, children, className, onClick }, restProps] = splitObject(this.props, + ['type', 'overlay', 'trigger', 'align', 'children', 'className', 'onClick']); + const cls = classNames({ + 'ant-dropdown-button': true, + [className]: !!className, + }); + return ( + + + + + + + ); + } +} diff --git a/components/dropdown/dropdown.jsx b/components/dropdown/dropdown.jsx deleted file mode 100644 index 8cb236f885..0000000000 --- a/components/dropdown/dropdown.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import Dropdown from 'rc-dropdown'; - -export default React.createClass({ - getDefaultProps() { - return { - transitionName: 'slide-up', - prefixCls: 'ant-dropdown', - }; - }, - render() { - const { overlay, ...otherProps } = this.props; - const menu = React.cloneElement(overlay, { - openTransitionName: 'zoom-big', - }); - return ( - - ); - } -}); diff --git a/components/dropdown/dropdown.tsx b/components/dropdown/dropdown.tsx new file mode 100644 index 0000000000..e10fa7280e --- /dev/null +++ b/components/dropdown/dropdown.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import RcDropdown from 'rc-dropdown'; + +export interface DropDownProps { + trigger: string[]; + overlay: React.ReactNode; + visible: boolean; + onVisibleChange: (visible: boolean) => void; +} + +export default class Dropdown extends React.Component { + static defaultProps = { + transitionName: 'slide-up', + prefixCls: 'ant-dropdown', + mouseEnterDelay: 0.15, + mouseLeaveDelay: 0.1, + }; + + render() { + return ; + } +} diff --git a/components/dropdown/index.en-US.md b/components/dropdown/index.en-US.md new file mode 100644 index 0000000000..7706a22c2d --- /dev/null +++ b/components/dropdown/index.en-US.md @@ -0,0 +1,41 @@ +--- +category: Components +type: Views +english: Dropdown +--- + +A dropdown list. + +## When To Use + +If there are too many operations to display, you can wrap them in a `Dropdown`. By clicking/hovering on the trigger, a dropdown menu should appear, which allows you to choose one option and execute relevant actions. + +## API + +### Dropdown + +| Property | Description | Type | Default | +|--------------|----------------|----------|--------------| +| trigger | the trigger mode which can excute the drop-down action | ['click'] or ['hover'] | ['hover'] | +| overlay | the dropdown menu | [Menu](/components/menu) | - | +| getPopupContainer | to set the container of the dropdown menu. The default is to create a `div` element in `body`, you can reset it to the scrolling area and make a relative reposition. [example](http://codepen.io/anon/pen/xVBOVQ?editors=001) | Function(triggerNode) | () => document.body | +| visible | determine whether the dropdown menu is visible | Boolean | - | +| onVisibleChange | a callback function takes an argument: `visible`, can be executed when the visible state is changing | Function | - | + +You can get the menu list by `antd.Menu`, and set a callback function `onSelect` for it if you need. The menu items and the dividers are also available, by using `antd.Menu.Item` and `antd.Menu.Divider` respectively. + +> Warning: You must set a unique `key` for `Menu.Item`. + + +### DropdownButton + +| Property | Description | Type | Default | +|--------------|----------------|----------|--------------| +| type | type of the button, the same as [Button](/components/button) | String | 'default' | +| onClick | a callback function, the same as [Button](/components/button), which will be executed when you click the button on the left | Function | - | +| trigger | the trigger mode which can excute the drop-down action | ['click'] or ['hover'] | ['hover'] | +| overlay | the dropdown menu | [Menu](/components/menu) | - | +| visible | determine whether the dropdown menu is visible | Boolean | - | +| onVisibleChange | a callback function takes an argument: `visible`, can be executed when the visible state is changing | Function | - | + + diff --git a/components/dropdown/index.md b/components/dropdown/index.md deleted file mode 100644 index 13db8872b8..0000000000 --- a/components/dropdown/index.md +++ /dev/null @@ -1,36 +0,0 @@ -# Dropdown - -- category: Components -- chinese: 下拉菜单 -- type: 展示 - ---- - -向下弹出的列表。 - -## 何时使用 - -当页面上的操作命令过多时,用此组件可以收纳操作元素。点击或移入触点,会出现一个下拉菜单。可在列表中进行选择,并执行相应的命令。 - -## API - -属性如下 - -| 成员 | 说明 | 类型 | 默认值 | -|-------------|------------------|--------------------|--------------| -| trigger | 触发下拉的行为 | ['click'] or ['hover'] | ['hover'] | -| overlay | 菜单节点 | [Menu](/components/menu) | 无 | - - -菜单可由 `antd.Menu` 取得,可设置 `onSelect` 回调,菜单还包括菜单项 `antd.Menu.Item`,分割线 `antd.Menu.Divider`。 - -> 注意: Menu.Item 必须设置唯一的 key 属性。 - -### DropdownButton - -| 成员 | 说明 | 类型 | 默认值 | -|-------------|------------------|--------------------|--------------| -| type | 按钮类型,和 [Button](/components/button) 一致 | String | 'default' | -| onClick | 点击左侧按钮的回调,和 [Button](/components/button) 一致 | Function | 无 | -| trigger | 触发下拉的行为 | "click" or "hover" | hover | -| overlay | 菜单节点 | [Menu](/components/menu) | 无 | diff --git a/components/dropdown/index.jsx b/components/dropdown/index.tsx similarity index 100% rename from components/dropdown/index.jsx rename to components/dropdown/index.tsx diff --git a/components/dropdown/index.zh-CN.md b/components/dropdown/index.zh-CN.md new file mode 100644 index 0000000000..e8be508a1c --- /dev/null +++ b/components/dropdown/index.zh-CN.md @@ -0,0 +1,39 @@ +--- +category: Components +chinese: 下拉菜单 +type: Views +english: Dropdown +--- + +向下弹出的列表。 + +## 何时使用 + +当页面上的操作命令过多时,用此组件可以收纳操作元素。点击或移入触点,会出现一个下拉菜单。可在列表中进行选择,并执行相应的命令。 + +## API + +属性如下 + +| 参数 | 说明 | 类型 | 默认值 | +|-------------|------------------|--------------------|--------------| +| trigger | 触发下拉的行为 | ['click'] or ['hover'] | ['hover'] | +| overlay | 菜单 | [Menu](/components/menu) | - | +| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](http://codepen.io/anon/pen/xVBOVQ?editors=001) | Function(triggerNode) | () => document.body | +| visible | 菜单是否显示 | Boolean | - | +| onVisibleChange | 菜单显示状态改变时调用,参数为 { visible } | Function | - | + +菜单可由 `antd.Menu` 取得,可设置 `onSelect` 回调,菜单还包括菜单项 `antd.Menu.Item`,分割线 `antd.Menu.Divider`。 + +> 注意: Menu.Item 必须设置唯一的 key 属性。 + +### DropdownButton + +| 参数 | 说明 | 类型 | 默认值 | +|-------------|------------------|--------------------|--------------| +| type | 按钮类型,和 [Button](/components/button) 一致 | String | 'default' | +| onClick | 点击左侧按钮的回调,和 [Button](/components/button) 一致 | Function | - | +| trigger | 触发下拉的行为 | ['click'] or ['hover'] | ['hover'] | +| overlay | 菜单 | [Menu](/components/menu) | - | +| visible | 菜单是否显示 | Boolean | - | +| onVisibleChange | 菜单显示状态改变时调用,参数为 { visible } | Function | - | diff --git a/style/components/dropdown.less b/components/dropdown/style/index.less similarity index 91% rename from style/components/dropdown.less rename to components/dropdown/style/index.less index 0a5bdc877a..86d44bf2fd 100644 --- a/style/components/dropdown.less +++ b/components/dropdown/style/index.less @@ -1,11 +1,13 @@ -@import "../mixins/index"; -@dropdown-prefix-cls: ~"@{css-prefix}dropdown"; +@import "../../style/themes/default"; +@import "../../style/mixins/index"; + +@dropdown-prefix-cls: ant-dropdown; .@{dropdown-prefix-cls} { position: absolute; left: -9999px; top: -9999px; - z-index: 1070; + z-index: @zindex-dropdown; display: block; font-size: 12px; font-weight: normal; @@ -14,7 +16,7 @@ &-wrap { position: relative; - .@{btn-prefix-cls} > .@{iconfont-css-prefix}-down { + .ant-btn > .@{iconfont-css-prefix}-down { .iconfont-size-under-12px(10px); } @@ -25,7 +27,7 @@ &-wrap-open { .anticon-down:before { - .rotate(180deg); + transform: rotate(180deg); } } @@ -90,6 +92,10 @@ border-radius: 0 0 @border-radius-base @border-radius-base; } + &:only-child { + border-radius: @border-radius-base; + } + &-divider { height: 1px; overflow: hidden; diff --git a/components/dropdown/style/index.tsx b/components/dropdown/style/index.tsx new file mode 100644 index 0000000000..416ec0177e --- /dev/null +++ b/components/dropdown/style/index.tsx @@ -0,0 +1,5 @@ +import '../../style/index.less'; +import './index.less'; + +// style dependencies +import '../../button/style'; diff --git a/components/form/Form.jsx b/components/form/Form.jsx deleted file mode 100644 index 4139a0e8e4..0000000000 --- a/components/form/Form.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; - -class Form extends React.Component { - getChildContext() { - return { - form: this.props.form, - }; - } - - render() { - const { prefixCls, className } = this.props; - const formClassName = classNames({ - [className]: !!className, - [`${prefixCls}-horizontal`]: this.props.horizontal, - [`${prefixCls}-inline`]: this.props.inline, - }); - - return ( -
- {this.props.children} -
- ); - } -} - -Form.propTypes = { - prefixCls: React.PropTypes.string, - horizontal: React.PropTypes.bool, - inline: React.PropTypes.bool, - form: React.PropTypes.object, - children: React.PropTypes.any, - onSubmit: React.PropTypes.func, -}; - -Form.defaultProps = { - prefixCls: 'ant-form', -}; - -Form.childContextTypes = { - form: React.PropTypes.object, -}; - -module.exports = Form; diff --git a/components/form/Form.tsx b/components/form/Form.tsx new file mode 100644 index 0000000000..140dad9637 --- /dev/null +++ b/components/form/Form.tsx @@ -0,0 +1,55 @@ +import * as React from 'react'; +import classNames from 'classnames'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import omit from 'object.omit'; +import warning from 'warning'; + +export default class Form extends React.Component { + static defaultProps = { + prefixCls: 'ant-form', + onSubmit(e) { + e.preventDefault(); + }, + }; + + static propTypes = { + prefixCls: React.PropTypes.string, + vertical: React.PropTypes.bool, + horizontal: React.PropTypes.bool, + inline: React.PropTypes.bool, + children: React.PropTypes.any, + onSubmit: React.PropTypes.func, + }; + + constructor(props) { + super(props); + + warning(!props.form, 'It is unnecessary to pass `form` to `Form` after antd@1.7.0.'); + } + + shouldComponentUpdate(...args) { + return PureRenderMixin.shouldComponentUpdate.apply(this, args); + } + + render() { + const { prefixCls, className, inline, horizontal, vertical } = this.props; + const formClassName = classNames({ + [`${prefixCls}`]: true, + [`${prefixCls}-horizontal`]: horizontal, + [`${prefixCls}-vertical`]: vertical, + [`${prefixCls}-inline`]: inline, + [className]: !!className, + }); + + const formProps = omit(this.props, [ + 'prefixCls', + 'className', + 'inline', + 'horizontal', + 'vertical', + 'form', + ]); + + return
; + } +} diff --git a/components/form/FormItem.jsx b/components/form/FormItem.tsx similarity index 54% rename from components/form/FormItem.jsx rename to components/form/FormItem.tsx index db90cddde3..5e237bfc09 100644 --- a/components/form/FormItem.jsx +++ b/components/form/FormItem.tsx @@ -1,56 +1,93 @@ -import React from 'react'; +import * as React from 'react'; import classNames from 'classnames'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import Row from '../row'; +import Col from '../col'; +import { FIELD_META_PROP } from './constants'; -function prefixClsFn(prefixCls, ...args) { - return args.map((s) => { - return `${prefixCls}-${s}`; - }).join(' '); -} +export default class FormItem extends React.Component { + static defaultProps = { + hasFeedback: false, + prefixCls: 'ant-form', + colon: true, + }; -class FormItem extends React.Component { - _getLayoutClass(colDef) { - if (!colDef) { - return ''; - } - const { span, offset } = colDef; - const col = span ? `col-${span}` : ''; - const offsetCol = offset ? ` col-offset-${offset}` : ''; - return col + offsetCol; + static propTypes = { + prefixCls: React.PropTypes.string, + label: React.PropTypes.node, + labelCol: React.PropTypes.object, + help: React.PropTypes.oneOfType([React.PropTypes.node, React.PropTypes.bool]), + validateStatus: React.PropTypes.oneOf(['', 'success', 'warning', 'error', 'validating']), + hasFeedback: React.PropTypes.bool, + wrapperCol: React.PropTypes.object, + className: React.PropTypes.string, + id: React.PropTypes.string, + children: React.PropTypes.node, + colon: React.PropTypes.bool, + }; + + static contextTypes = { + form: React.PropTypes.object, + }; + + shouldComponentUpdate(...args) { + return PureRenderMixin.shouldComponentUpdate.apply(this, args); } getHelpMsg() { const context = this.context; const props = this.props; if (props.help === undefined && context.form) { - return (context.form.getFieldError(this.getId()) || []).join(', '); + return this.getId() ? (context.form.getFieldError(this.getId()) || []).join(', ') : ''; } return props.help; } + getOnlyControl() { + const children = React.Children.toArray(this.props.children); + const child = children.filter((c) => { + return c.props && FIELD_META_PROP in c.props; + })[0]; + return child !== undefined ? child : null; + } + + getChildProp(prop) { + const child = this.getOnlyControl(); + return child && child.props && child.props[prop]; + } + getId() { - return this.props.children.props && this.props.children.props.id; + return this.getChildProp('id'); } getMeta() { - return this.props.children.props && this.props.children.props.__meta; + return this.getChildProp(FIELD_META_PROP); } renderHelp() { - const props = this.props; - const prefixCls = props.prefixCls; + const prefixCls = this.props.prefixCls; const help = this.getHelpMsg(); - return ( -
- { help } + return help ? ( +
+ {help}
- ); + ) : null; + } + + renderExtra() { + const { prefixCls, extra } = this.props; + return extra ? ( + {extra} + ) : null; } getValidateStatus() { const { isFieldValidating, getFieldError, getFieldValue } = this.context.form; const field = this.getId(); - + if (!field) { + return ''; + } if (isFieldValidating(field)) { return 'validating'; } else if (!!getFieldError(field)) { @@ -58,7 +95,6 @@ class FormItem extends React.Component { } else if (getFieldValue(field) !== undefined) { return 'success'; } - return ''; } @@ -91,9 +127,9 @@ class FormItem extends React.Component { renderWrapper(children) { const wrapperCol = this.props.wrapperCol; return ( -
+ {children} -
+ ); } @@ -117,14 +153,21 @@ class FormItem extends React.Component { props.required; const className = classNames({ - [this._getLayoutClass(labelCol)]: true, [`${props.prefixCls}-item-required`]: required, }); + // remove user input colon + let label = props.label; + if (typeof label === 'string' && label.trim() !== '') { + label = props.label.replace(/[:|:]\s*$/, ''); + } + return props.label ? ( - + + + ) : null; } @@ -142,7 +185,7 @@ class FormItem extends React.Component { this.renderValidateWrapper( children, this.renderHelp(), - props.extra + this.renderExtra() ) ), ]; @@ -151,16 +194,18 @@ class FormItem extends React.Component { renderFormItem(children) { const props = this.props; const prefixCls = props.prefixCls; + const style = props.style; const itemClassName = { [`${prefixCls}-item`]: true, [`${prefixCls}-item-with-help`]: !!this.getHelpMsg(), + [`${prefixCls}-item-no-colon`]: !props.colon, [`${props.className}`]: !!props.className, }; return ( -
+ {children} -
+ ); } @@ -169,27 +214,3 @@ class FormItem extends React.Component { return this.renderFormItem(children); } } - -FormItem.propTypes = { - prefixCls: React.PropTypes.string, - label: React.PropTypes.node, - labelCol: React.PropTypes.object, - help: React.PropTypes.oneOfType([React.PropTypes.node, React.PropTypes.bool]), - validateStatus: React.PropTypes.oneOf(['', 'success', 'warning', 'error', 'validating']), - hasFeedback: React.PropTypes.bool, - wrapperCol: React.PropTypes.object, - className: React.PropTypes.string, - id: React.PropTypes.string, - children: React.PropTypes.node, -}; - -FormItem.defaultProps = { - hasFeedback: false, - prefixCls: 'ant-form', -}; - -FormItem.contextTypes = { - form: React.PropTypes.object, -}; - -module.exports = FormItem; diff --git a/components/form/ValueMixin.jsx b/components/form/ValueMixin.tsx similarity index 80% rename from components/form/ValueMixin.jsx rename to components/form/ValueMixin.tsx index 7450ddd687..53083b2f62 100644 --- a/components/form/ValueMixin.jsx +++ b/components/form/ValueMixin.tsx @@ -1,3 +1,4 @@ +import assign from 'object-assign'; const ValueMixin = { setValue(field, e) { let v = e; @@ -13,10 +14,7 @@ const ValueMixin = { const newFormData = {}; newFormData[field] = v; this.setState({ - formData: { - ...this.state.formData, - ...newFormData, - }, + formData: assign({}, this.state.formData, newFormData), }); }, }; diff --git a/components/form/constants.tsx b/components/form/constants.tsx new file mode 100644 index 0000000000..27b32ad273 --- /dev/null +++ b/components/form/constants.tsx @@ -0,0 +1 @@ +export const FIELD_META_PROP = 'data-__meta'; diff --git a/components/form/demo/advanced-search-form.md b/components/form/demo/advanced-search-form.md index 7a064b1dda..2202a348de 100644 --- a/components/form/demo/advanced-search-form.md +++ b/components/form/demo/advanced-search-form.md @@ -1,115 +1,128 @@ -# 高级搜索 +--- +order: 10 +title: + zh-CN: 高级搜索 + en-US: Advanced search +--- -- order: 10 +## zh-CN 三列栅格式的表单排列方式,常用于数据表格的高级搜索。 有部分定制的样式代码,由于输入标签长度不确定,需要根据具体情况自行调整。 ---- +## en-US + +Three columns layout is often used for advanced searching of data table. + +Because the width of label is not fixed, you may need to adjust it by customizing its style. + ````jsx -import { Form, Input, Row, Col, Button } from 'antd'; +import { Form, Input, Row, Col, Button, DatePicker } from 'antd'; const FormItem = Form.Item; ReactDOM.render( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
, mountNode); ```` ````css -/* 定制样式 */ +/* custom style */ -.advanced-search-form { +.ant-advanced-search-form { padding: 16px 8px; background: #f8f8f8; border: 1px solid #d9d9d9; border-radius: 6px; } -/* 由于输入标签长度不确定,所以需要微调使之看上去居中 */ -.advanced-search-form > .row { - margin-left: -10px; +/* because the label length is variable, you may need to adjust the left edge to have the form centered */ + +.ant-advanced-search-form > .ant-row { + position: relative; + left: -6px; } -.advanced-search-form > .row > .col-8 { - padding: 0 8px; -} - -.advanced-search-form .ant-form-item { - margin-bottom: 16px; -} - -.advanced-search-form .ant-btn + .ant-btn { +.ant-advanced-search-form .ant-btn + .ant-btn { margin-left: 8px; } ```` diff --git a/components/form/demo/dynamic-form-item.md b/components/form/demo/dynamic-form-item.md new file mode 100644 index 0000000000..1afcccf2b3 --- /dev/null +++ b/components/form/demo/dynamic-form-item.md @@ -0,0 +1,95 @@ +--- +order: 15 +title: + zh-CN: 动态增减表单项 + en-US: Dynamic form item +--- + +## zh-CN + +动态增加、减少表单项。 + +## en-US + +Add or remove form items dynamically. + +````jsx +import { Form, Input, Button } from 'antd'; + +let uuid = 0; +let Demo = React.createClass({ + remove(k) { + const { form } = this.props; + // can use data-binding to get + let keys = form.getFieldValue('keys'); + keys = keys.filter((key) => { + return key !== k; + }); + // can use data-binding to set + form.setFieldsValue({ + keys, + }); + }, + add() { + uuid++; + const { form } = this.props; + // can use data-binding to get + let keys = form.getFieldValue('keys'); + keys = keys.concat(uuid); + // can use data-binding to set + // important! notify form to detect changes + form.setFieldsValue({ + keys, + }); + }, + submit(e) { + e.preventDefault(); + this.props.form.validateFields((errors, values) => { + if (errors) { + console.log(errors); + } + console.log(values); + }); + }, + render() { + const { getFieldProps, getFieldValue } = this.props.form; + getFieldProps('keys', { + initialValue: [0], + }); + + const formItemLayout = { + labelCol: { span: 6 }, + wrapperCol: { span: 18 }, + }; + + const formItems = getFieldValue('keys').map((k) => { + return ( + + + + + ); + }); + return ( +
+ {formItems} + + + + +
+ ); + }, +}); + +Demo = Form.create()(Demo); + +ReactDOM.render(, mountNode); +```` diff --git a/components/form/demo/form-controls.md b/components/form/demo/form-controls.md index d616e0ede6..d5df5d8cd4 100644 --- a/components/form/demo/form-controls.md +++ b/components/form/demo/form-controls.md @@ -1,12 +1,21 @@ -# 表单控件 +--- +order: 3 +title: + zh-CN: 表单控件 + en-US: Form controls +--- -- order: 3 +## zh-CN 展示所有支持的表单控件。 -`注`: 输入框:只有正确设置了 type 属性的输入控件才能被赋予正确的样式。 +**注**: 输入框:只有正确设置了 type 属性的输入控件才能被赋予正确的样式。 ---- +## en-US + +A list off all the controls that can be used with form. + +**Note**: Input control: Only if set correct type for it, then it will be set correct style ````jsx import { Form, Input, Select, Checkbox, Radio } from 'antd'; @@ -22,73 +31,67 @@ ReactDOM.render(
+ wrapperCol={{ span: 14 }} + > + wrapperCol={{ span: 14 }} + > + wrapperCol={{ span: 14 }} + > - - - + wrapperCol={{ span: 18 }} + > + item 1 + item 2 + item 3 (disabled) - - - + wrapperCol={{ span: 18 }} + > + item 1 + item 2 + item 3 - - A - B - C - D - + wrapperCol={{ span: 18 }} + > + + A + B + C + D +
, mountNode); diff --git a/components/form/demo/form-in-modal.md b/components/form/demo/form-in-modal.md new file mode 100644 index 0000000000..d40aeb8aa9 --- /dev/null +++ b/components/form/demo/form-in-modal.md @@ -0,0 +1,73 @@ +--- +order: 14 +title: + zh-CN: 与 Modal 配合使用 + en-US: To use with modal +--- + +## zh-CN + +在 Modal 中使用 Form,当点击 Modal 的确定时,调用 `this.props.form.getFieldsValue` 获取表单内的值。 + +## en-US + +If you use Form in Modal, when you click the Modal, it could invoke `this.props.form.getFieldsValue` to get values of form. + +````jsx +import { Button, Form, Input, Modal } from 'antd'; +const createForm = Form.create; +const FormItem = Form.Item; + +let Demo = React.createClass({ + getInitialState() { + return { visible: false }; + }, + + handleSubmit() { + console.log(this.props.form.getFieldsValue()); + this.hideModal(); + }, + + showModal() { + this.setState({ visible: true }); + }, + + hideModal() { + this.setState({ visible: false }); + }, + + render() { + const { getFieldProps } = this.props.form; + + const formItemLayout = { + labelCol: { span: 4 }, + wrapperCol: { span: 20 }, + }; + return ( +
+ + +
+ + + + + + +
+
+
+ ); + }, +}); + +Demo = createForm()(Demo); + +ReactDOM.render(, mountNode); +```` diff --git a/components/form/demo/horizontal-form.md b/components/form/demo/horizontal-form.md index 75c7555022..225d4c868f 100644 --- a/components/form/demo/horizontal-form.md +++ b/components/form/demo/horizontal-form.md @@ -1,20 +1,27 @@ -# 典型表单 +--- +order: 2 +title: + zh-CN: 典型表单 + en-US: Horizontal form +--- -- order: 2 +## zh-CN 示例展示了如何通过使用 `Form.create` 来获取和更新表单提交的数值。 ---- +## en-US + +How to use `Form.create` to get and update values of form. ````jsx -import { Form, Input, Button, Checkbox, Radio, Row, Col, Tooltip, Icon } from 'antd'; +import { Form, Input, Button, Checkbox, Radio, Tooltip, Icon } from 'antd'; const FormItem = Form.Item; const RadioGroup = Radio.Group; let Demo = React.createClass({ handleSubmit(e) { e.preventDefault(); - console.log('收到表单值:', this.props.form.getFieldsValue()); + console.log('Received values of form:', this.props.form.getFieldsValue()); }, render() { @@ -27,43 +34,44 @@ let Demo = React.createClass({
-

大眼萌 minion

+ label="User name" + > +

Big eye minion

- + label="Password" + > + + label="Gender" + > - 男的 - 女的 + male + female - + label="remarks" + help="Please input something" + > + 卖身华府 :}> - + label={Sold myself } + > + agree + + + - - - - -
); - } + }, }); Demo = Form.create()(Demo); diff --git a/components/form/demo/inline-form.md b/components/form/demo/inline-form.md index ab6b7c8fba..2151b577d0 100644 --- a/components/form/demo/inline-form.md +++ b/components/form/demo/inline-form.md @@ -1,10 +1,17 @@ -# 平行排列 +--- +order: 1 +title: + zh-CN: 平行排列 + en-US: Inline form +--- -- order: 1 +## zh-CN 行内排列,常用于登录界面。 ---- +## en-US + +Inline form is often used for login. ````jsx import { Form, Input, Button, Checkbox } from 'antd'; @@ -13,7 +20,7 @@ const FormItem = Form.Item; let Demo = React.createClass({ handleSubmit(e) { e.preventDefault(); - console.log('收到表单值:', this.props.form.getFieldsValue()); + console.log('Received values of form:', this.props.form.getFieldsValue()); }, render() { @@ -21,25 +28,26 @@ let Demo = React.createClass({ return (
- + label="Account" + > + - + label="Password" + > + - + Remember me - +
); - } + }, }); Demo = Form.create()(Demo); diff --git a/components/form/demo/input-group.md b/components/form/demo/input-group.md index c2c1c22344..3f546e2363 100644 --- a/components/form/demo/input-group.md +++ b/components/form/demo/input-group.md @@ -1,54 +1,65 @@ -# 输入框组合 +--- +order: 4 +title: + zh-CN: 输入框组合 + en-US: Input group +--- -- order: 4 +## zh-CN 各类输入框的组合展现。 ---- +## en-US + +Input group of different type input controls. ````jsx -import { Form, Input, Select, Row, Col } from 'antd'; +import { Form, Input, Select, Col } from 'antd'; const FormItem = Form.Item; const InputGroup = Input.Group; const Option = Select.Option; +const selectAfter = ( + +); + ReactDOM.render(
+ wrapperCol={{ span: 16 }} + > + validateStatus="success" + wrapperCol={{ span: 16 }} + > - - -
- -
-
+ wrapperCol={{ span: 16 }} + > +
+ wrapperCol={{ span: 16 }} + > @@ -66,30 +77,27 @@ ReactDOM.render( - + wrapperCol={{ span: 16 }} + > +

--

- - - - - - - - - - - - + + -
+ + + + + + +
diff --git a/components/form/demo/input.md b/components/form/demo/input.md deleted file mode 100644 index 9deb5796d9..0000000000 --- a/components/form/demo/input.md +++ /dev/null @@ -1,31 +0,0 @@ -# Input 输入框 - -- order: 0 - -我们为 `` 输入框定义了三种尺寸(大、默认、小),具体使用详见 [API](/components/form/#input)。 - -注意: 在表单里面,我们只使用**大尺寸**, 即高度为 **32px**,作为唯一的尺寸。 - ---- - - -````jsx -import { Row, Col, Input } from 'antd'; -const InputGroup = Input.Group; - -ReactDOM.render( - - - - - - - - - - - - - -, mountNode); -```` diff --git a/components/form/demo/mix.md b/components/form/demo/mix.md index 8364995fed..db925da493 100644 --- a/components/form/demo/mix.md +++ b/components/form/demo/mix.md @@ -1,23 +1,43 @@ -# 表单组合 +--- +order: 5 +title: + zh-CN: 表单组合 + en-US: mix +--- -- order: 5 +## zh-CN 集中营,展示和表单相关的其他 ant-design 组件。 ---- +## en-US + +A mix to demonstrate others ant-design component related to form. ````jsx import { Form, Select, InputNumber, DatePicker, TimePicker, Switch, Radio, - Slider, Button, Row, Col, Upload, Icon } from 'antd'; + Cascader, Slider, Button, Col, Upload, Icon } from 'antd'; const FormItem = Form.Item; const Option = Select.Option; const RadioButton = Radio.Button; const RadioGroup = Radio.Group; +const areaData = [{ + value: 'shanghai', + label: 'Shanghai', + children: [{ + value: 'shanghaishi', + label: 'Shanghai', + children: [{ + value: 'pudongxinqu', + label: 'Pudong New District', + }], + }], +}]; + let Demo = React.createClass({ handleSubmit(e) { e.preventDefault(); - console.log('收到表单值:', this.props.form.getFieldsValue()); + console.log('Received the values of form', this.props.form.getFieldsValue()); }, normFile(e) { @@ -32,47 +52,54 @@ let Demo = React.createClass({ return (
+ wrapperCol={{ span: 10 }} + > - 台机器 + {...getFieldProps('inputNumber', { initialValue: 3 })} + /> + machines -

唧唧复唧唧木兰当户织呀

+ wrapperCol={{ span: 10 }} + > +

O, wind, if winter comes, can spring be far behind?

- 链接文字 + link

- + required + > + + required + > + required + > + help={isFieldValidating('name') ? 'validating...' : (getFieldError('name') || []).join(', ')} + > + - + label="Email" + hasFeedback + > + + label="Password" + hasFeedback + > + onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop} + /> - + label="Confirm password" + hasFeedback + > + - + label="remark" + > + - +     - +
); - } -} + }, +}); BasicDemo = createForm()(BasicDemo); diff --git a/components/form/demo/validate-customized.md b/components/form/demo/validate-customized.md index d93b7b6cf2..92aa9a52df 100644 --- a/components/form/demo/validate-customized.md +++ b/components/form/demo/validate-customized.md @@ -1,15 +1,24 @@ -# 自定义校验规则 +--- +order: 13 +title: + zh-CN: 自定义校验规则 + en-US: Customized validation +--- -- order: 13 +## zh-CN 密码校验实例。 这里使用了 `this.props.form.validateFields` 方法,在对第一次输入的密码进行校验时会触发二次密码的校验。 ---- +## en-US + +Customized validation for Password. + +To use `this.props.form.validateFields` method, when validating first password you enter will trigger the seconcd password validation. ````jsx -import { Button, Form, Input, Row, Col, Modal } from 'antd'; +import { Button, Form, Input, Row, Col } from 'antd'; import classNames from 'classnames'; const createForm = Form.create; const FormItem = Form.Item; @@ -21,11 +30,11 @@ function noop() { let Demo = React.createClass({ getInitialState() { return { - passBarShow: false, // 是否显示密码强度提示条 + dirty: false, + passBarShow: false, // Whether to display a tooltip of password strength rePassBarShow: false, - passStrength: 'L', // 密码强度 + passStrength: 'L', // Password strength rePassStrength: 'L', - visible: false, }; }, @@ -37,14 +46,13 @@ let Demo = React.createClass({ } console.log('Submit!!!'); console.log(values); - this.setState({ visible: false }); }); }, getPassStrenth(value, type) { if (value) { let strength; - // 密码强度的校验规则自定义,这里只是做个简单的示例 + // Customized the password strength, here is just a simple example if (value.length < 6) { strength = 'L'; } else if (value.length <= 9) { @@ -66,19 +74,11 @@ let Demo = React.createClass({ } }, - showModal() { - this.setState({ visible: true }); - }, - - hideModal() { - this.setState({ visible: false }); - }, - checkPass(rule, value, callback) { const form = this.props.form; this.getPassStrenth(value, 'pass'); - if (form.getFieldValue('pass')) { + if (form.getFieldValue('pass') && this.state.dirty) { form.validateFields(['rePass'], { force: true }); } @@ -90,7 +90,7 @@ let Demo = React.createClass({ this.getPassStrenth(value, 'rePass'); if (value && value !== form.getFieldValue('pass')) { - callback('两次输入密码不一致!'); + callback('Two passwords you enter is inconsistent!'); } else { callback(); } @@ -102,12 +102,12 @@ let Demo = React.createClass({ 'ant-pwd-strength': true, 'ant-pwd-strength-low': strength === 'L', 'ant-pwd-strength-medium': strength === 'M', - 'ant-pwd-strength-high': strength === 'H' + 'ant-pwd-strength-high': strength === 'H', }); const level = { - L: '低', - M: '中', - H: '高' + L: 'Low', + M: 'Middle', + H: 'High', }; return ( @@ -127,65 +127,62 @@ let Demo = React.createClass({ render() { const { getFieldProps } = this.props.form; - // 如果觉得在 JSX 中写 `getFieldProps` 会影响阅读,可以先用变量保存 `getFieldProps` 的返回值。 const passProps = getFieldProps('pass', { rules: [ - { required: true, whitespace: true, message: '请填写密码' }, - { validator: this.checkPass } - ] + { required: true, whitespace: true, message: 'Please enter your password' }, + { validator: this.checkPass }, + ], + onChange: (e) => { + console.log('Your password is stolen in this way', e.target.value); + }, }); const rePassProps = getFieldProps('rePass', { rules: [{ required: true, whitespace: true, - message: '请再次输入密码', + message: 'Please confirm your password', }, { validator: this.checkPass2, }], }); - const formItemLayout = { - labelCol: { span: 6 }, - wrapperCol: { span: 18 }, - }; return (
- - -
- - - - - - - - {this.state.passBarShow ? this.renderPassStrengthBar('pass') : null} - - - - - - - - - - - {this.state.rePassBarShow ? this.renderPassStrengthBar('rePass') : null} - - -
-
+
+ + + + { + const value = e.target.value; + this.setState({ dirty: this.state.dirty || !!value }); + }} + /> + + + + {this.state.passBarShow ? this.renderPassStrengthBar('pass') : null} + + + + + + + + + + {this.state.rePassBarShow ? this.renderPassStrengthBar('rePass') : null} + + + +
); - } + }, }); Demo = createForm()(Demo); diff --git a/components/form/demo/validate-other.md b/components/form/demo/validate-other.md index 68c0240720..a063d0c2bf 100644 --- a/components/form/demo/validate-other.md +++ b/components/form/demo/validate-other.md @@ -1,13 +1,20 @@ -# 校验其他组件 +--- +order: 12 +title: + zh-CN: 校验其他组件 + en-US: Others components related to validation +--- -- order: 12 +## zh-CN 提供以下组件表单域的校验:`Select` `Radio` `DatePicker` `InputNumber` `Cascader`。在 submit 时使用 `validateFieldsAndScroll`,进行校验,可以自动把不在可见范围内的校验不通过的菜单域滚动进可见范围。 ---- +## en-US + +Provide validation for fllowing input filed: `Select` `Radio` `DatePicker` `InputNumber` `Cascader`. To use `validateFieldsAndScroll` with form validation, it will scroll the form to the failed input field which is not in visible area. ````jsx -import { Select, Radio, Checkbox, Button, DatePicker, InputNumber, Form, Cascader } from 'antd'; +import { Select, Radio, Checkbox, Button, DatePicker, TimePicker, InputNumber, Form, Cascader, Icon } from 'antd'; const Option = Select.Option; const RadioGroup = Radio.Group; const createForm = Form.create; @@ -41,7 +48,7 @@ let Demo = React.createClass({ checkBirthday(rule, value, callback) { if (value && value.getTime() >= Date.now()) { - callback(new Error('你不可能在未来出生吧!')); + callback(new Error("You can't be born in the future!")); } else { callback(); } @@ -49,7 +56,7 @@ let Demo = React.createClass({ checkPrime(rule, value, callback) { if (value !== 11) { - callback(new Error('8~12之间的质数明明是11啊!')); + callback(new Error('The prime number between 8 to 12 is obiviously 11!')); } else { callback(); } @@ -58,38 +65,44 @@ let Demo = React.createClass({ render() { const address = [{ value: 'zhejiang', - label: '浙江', + label: 'Zhe Jiang', children: [{ value: 'hangzhou', - label: '杭州', + label: 'Hang Zhou', }], }]; const { getFieldProps } = this.props.form; const selectProps = getFieldProps('select', { rules: [ - { required: true, message: '请选择您的国籍' } + { required: true, message: 'Please select your country' }, ], }); const multiSelectProps = getFieldProps('multiSelect', { rules: [ - { required: true, message: '请选择您喜欢的颜色', type: 'array' }, - ] + { required: true, message: 'Please select your favourite colors', type: 'array' }, + ], }); const radioProps = getFieldProps('radio', { rules: [ - { required: true, message: '请选择您的性别' } - ] + { required: true, message: 'Please select your gender' }, + ], }); const birthdayProps = getFieldProps('birthday', { rules: [ { required: true, type: 'date', - message: '你的生日是什么呢?', + message: 'When is your birthday?', }, { validator: this.checkBirthday, - } - ] + }, + ], + }); + const timeProps = getFieldProps('time', { + getValueFromEvent: (value, timeString) => timeString, + rules: [ + { required: true, message: 'Please select the time' }, + ], }); const primeNumberProps = getFieldProps('primeNumber', { rules: [{ validator: this.checkPrime }], @@ -105,74 +118,90 @@ let Demo = React.createClass({
- + + + + + - + + + + + + label="Gender" + > - - + male + female + Temporarily does not support ohter gender + label="Hobby" + > 吃饭饭   + })}>eat 睡觉觉   + })}>sleeping 打豆豆   + })}>dozen doug + label="Birthday" + > + label="Select the time" + > + + + + + label="Please select address" + > - + wrapperCol={{ span: 12, offset: 7 }} + > +     - +
); diff --git a/components/form/demo/validate-static.md b/components/form/demo/validate-static.md index c586ddf348..c5f4002fc2 100644 --- a/components/form/demo/validate-static.md +++ b/components/form/demo/validate-static.md @@ -1,16 +1,29 @@ -# 校验提示 +--- +order: 6 +title: + zh-CN: 校验提示 + en-US: Validation message +--- -- order: 6 +## zh-CN 我们为表单控件定义了三种校验状态,为 `` 定义 `validateStatus` 属性即可。 -validateStatus: ['success', 'warning', 'error', 'validating']。 +validateStatus: 'success', 'warning', 'error', 'validating'。 另外为输入框添加反馈图标,设置 `` 的 `hasFeedback` 属性值为 `true` 即可。 **注意**: 反馈图标只对 `` 有效。 ---- +## en-US + +We provide three kinds of validation status for form. You can use it just define `validateStatus` property on ``. + +validateStatus: 'success', 'warning', 'error', 'validating'。 + +To set `hasFeedback` property to `true` enable to display feed icon of input control. + +**PS**: Feed icon is just available for ``. ````jsx import { Form, Input, DatePicker, Col } from 'antd'; @@ -19,66 +32,73 @@ const FormItem = Form.Item; ReactDOM.render(
- + help="Please enter a combination of numbers and alphabets" + > + - + validateStatus="warning" + > + - + help="The information is being validated..." + > + - + validateStatus="success" + > + - + validateStatus="warning" + > + - + help="Please enter a combination of numbers and alphabets" + > + + help + > - + @@ -91,25 +111,6 @@ ReactDOM.render( - - - - - - -

-

- - - - - -

请选择正确日期

- -
, mountNode); ```` diff --git a/components/form/index.en-US.md b/components/form/index.en-US.md new file mode 100644 index 0000000000..d5b68f2f16 --- /dev/null +++ b/components/form/index.en-US.md @@ -0,0 +1,122 @@ +--- +category: Components +type: Form Controls +cols: 1 +english: Form +--- + +Forms are used to collect, validate, and submit the user input. They contain one or more form items. +There are many types of form items including checkbox, radio, input, select, and more. + +## Form + +You can align the controls of a `form` using one of the following attributes: + +- `horizontal`:to horizontally align the `label`s and controls of the fields. +- `inline`:to render the labels and controls of the fields in one line (by setting the display property of form controls to `inline-block`). + +## Form fields + +A form consists of one or more form fields whose type includes input, textarea, checkbox, radio, select, tag, and more. +A form field is defined using ``. + +```jsx + + {children} + +``` + +> PS:By default, large size controls are used within a form. + +## API + +### Form + +**more example [rc-form](http://react-component.github.io/form/)**。 + +| Property | Description | Type | Default Value | +|-----------|------------------------------------------|------------|---------| +| form | Decorated by `Form.create()` will be automatically set `this.props.form` property, so just pass to form, you don't need to set it by yourself after 1.7.0. | object | n/a +| vertical | Use vertical layout. | boolean | false | +| horizontal | Use horizontal layout. | boolean | false | +| inline | Use inline alignment. | boolean | false | +| onSubmit | Defines a function will be called if form data validation is successful. | Function(e:Event) | | +| prefixCls | Set the CSS class name of form component (optional). | string | 'ant-form' | + +### Form.create(options) + +How to use: + +```jsx +class CustomizedForm extends React.Component {} + +CustomizedForm = Form.create({})(CustomizedForm); +``` + +The following `options` are available: + +| Property | Description | Type | +|-----------|------------------------------------------|------------| +| onFieldsChange | Specify a function that will be called when the value a `Form.Item` gets changed. Usage example: saving the field's value to Redux store. | Function(props, fields) | +| mapPropsToFields | Convert props to corresponding field value. Usage example: reading the values from Redux store. | Function(props): Object{ fieldName: Object{ value } } | + +If the form has been decorated by `Form.create` then it has `this.props.form` property. `this.props.form` provides some APIs as follows: + +| Property | Description | Type | +|-----------|------------------------------------------|------------| +| getFieldsValue | Get the specified fields' values. If you don't specify a parameter, you will get all fields' values. | Function([fieldNames: string[]]) | +| getFieldValue | Get the value of a field. | Function(fieldName: string) | +| setFieldsValue | Set the value of a field. | Function(obj: object) | +| setFields | Set the value and error of a field. | Function(obj: object) | +| validateFields | Validate the specified fields and get theirs values and errors. | Function([fieldNames: string[]], [options: object], callback: Function(errors, values)) | +| 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` | +| getFieldError | Get the error of a field. | Function(name) | +| isFieldValidating | Check if the specified field is being validated. | Function(name) | +| resetFields | Reset the specified fields' value and status. If you don't specify a parameter, all the fields will be reset. | Function([names: string[]]) | +| getFieldProps | Two-way binding for form, please read below for details. | | + +### this.props.form.getFieldProps(id, options) + +#### Special attention + +If you use `react@<15.3.0`, then, you can't use `getFieldProps` in functional components: https://github.com/facebook/react/pull/6534 + +The return value of `getFieldProps` contains `id`、`value`(or any other props `valuePropName` that you specified),`ref`,`onChange`(or any other `trigger` `validateTrigger` that you specified), **shouldn't set same property again** in order to avoid conflict. If you concerntate on the details on return value, you can print them to console by `console.log`. + +> Don't use `defaultValue` in form, please use `initialValue` instead of it. + +#### getFieldProps options + +| Property | Description | Type | Default Value | +|-----------|-----------------------------------------|-----|--------| +| options.id | The unique identifier is required. | string | | +| options.valuePropName | Props of children node, for exmaple, the prop of Switch is 'checked'. | string | 'value' | +| options.initialValue | You can specify initial value, type, optional value of children node. | | n/a | +| options.trigger | When to collect the value of children node | string | 'onChange' | +| options.getValueFromEvent | To convert parameters of onChange to the value of control, for example, set value of DatePicker: `(date, dateString) => dateString` | function(..args) | [reference](https://github.com/react-component/form#optiongetvaluefromevent) | +| options.validateTrigger | When to validate the value of children node. | string | 'onChange' | +| options.rules | Includes validation rules. Please refer to [async-validator](https://github.com/yiminghe/async-validator) for details. | array | n/a | +| options.onXXX | Because `getFieldProps` will replace events like `onChange`, `trigger`, `validateTrigger`, if you still want to bind these events, you may set them in `options` | function | n/a | +| options.exclusive | Whether it is exclusive with other controls, particularly for Radio. | boolean | false | + +### Form.Item + +> If Form.Item has multiple children, `help`, `required`, and `validateStatus` can't be generated automatically. + +| Property | Description | Type | Optional | Default Value | +|-----------|------------------------------------------|-----------|-------|--------| +| label | Label text | string | | | +| labelCol | The layout of label. You can set `span` `offset` to something like `{span: 3, offset: 12}` same as with `` | object | | | +| wrapperCol | The layout for input controls. Same as `labelCol` | object | | | +| help | The prompt message. If not provided, the prompt message will be generated by the validation rule. | string | | | +| extra | The extra prompt message. It is similar to help. Usage example: to display error message and prompt message at the same time. | string | | | +| required | Whether provided or not, it will be generated by the validation rule. | bool | | false | +| validateStatus | The validation status. If not provided, it will be generated by validation rule | string | 'success' 'warning' 'error' 'validating' | | +| hasFeedback | Used with `validateStatus`, this option specifies the validation status icon. Recommended to be used only with `Input`. | bool | | false | +| prefixCls | The CSS class name of form item (optional). | string | | 'ant-form' | + + diff --git a/components/form/index.jsx b/components/form/index.jsx deleted file mode 100644 index f34cdc3831..0000000000 --- a/components/form/index.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import Form from './Form'; -import FormItem from './FormItem'; -import ValueMixin from './ValueMixin'; -import Input from '../input'; -import createDOMForm from 'rc-form/lib/createDOMForm'; - -Form.create = (o = {}) => { - const options = { - ...o, - fieldNameProp: 'id', - fieldMetaProp: '__meta', - }; - - return createDOMForm(options); -}; -Form.Item = FormItem; - -// @Deprecated -Form.ValueMixin = ValueMixin; - -// 对于 import { Form, Input } from 'antd/lib/form/'; -// 的方式做向下兼容 -// https://github.com/ant-design/ant-design/pull/566 -Form.Form = Form; -Form.Input = Input; - -export default Form; diff --git a/components/form/index.md b/components/form/index.md deleted file mode 100644 index d680a6de83..0000000000 --- a/components/form/index.md +++ /dev/null @@ -1,140 +0,0 @@ -# Form - -- category: Components -- chinese: 表单 -- type: 表单 -- cols: 1 - ---- - -具有数据收集、校验和提交功能的表单,包含复选框、单选框、输入框、下拉选择框等元素。 - - -## 表单 - -我们为 `form` 提供了以下两种排列方式: - -- 水平排列:可以实现 `label` 标签和表单控件的水平排列; -- 行内排列:使其表现为 `inline-block` 级别的控件。 - -## 表单域 - -表单一定会包含表单域,表单域可以是输入控件,标准表单域,标签,下拉菜单,文本域等。 - -这里我们分别封装了表单域 `` 和输入控件 ``。 - -```html - - {children} - -``` - -## Input 输入框 - -```html - -``` - -> 注:标准表单中一律使用大号控件。 - -## API - -### Form - -更多示例参考 [rc-form](http://react-component.github.io/form/)。 - -| 参数 | 说明 | 类型 | 可选值 |默认值 | -|-----------|------------------------------------------|------------|-------|--------| -| form | 经 `Form.create()` 包装过的组件会自带 `this.props.form` 属性,直接传给 Form 即可 | object | | 无 | -| horizontal | 水平排列布局 | boolean | | false | -| inline | 行内排列布局 | boolean | | false | -| onSubmit | 数据验证成功后回调事件 | Function(e:Event) | | | -| prefixCls | 样式类名,默认为 ant-form,通常您不需要设置 | string | | 'ant-form' | - -### Form.create(options) - -使用方式如下: - -```jsx -class CustomizedForm extends React.Component {} - -CustomizedForm = Form.create({})(CustomizedForm); -``` - -`options` 的配置项如下。 - -| 参数 | 说明 | 类型 | 可选值 |默认值 | -|-----------|------------------------------------------|------------|-------|--------| -| onFieldsChange | 当 `Form.Item` 子节点的值发生改变时触发,可以把对应的值转存到 Redux store | Function(props, fields) | | | -| mapPropsToFields | 把 props 转为对应的值,可用于把 Redux store 中的值读出 | Function(props) | | | | - -经过 `Form.create` 包装的组件将会自带 `this.props.form` 属性,`this.props.form` 提供的 API 如下: - -| 参数 | 说明 | 类型 | 可选值 |默认值 | -|-----------|------------------------------------------|------------|-------|--------| -| getFieldsValue | 获取一组输入控件的值,如不传入参数,则获取全部组件的值 | Function([fieldNames: string[]]) | | | -| getFieldValue | 获取一个输入控件的值 | Function(fieldName: string) | | | -| setFieldsValue | 设置一组输入控件的值 | Function(obj: object) | | | -| setFields | 设置一组输入控件的值与 Error | Function(obj: object) | | | -| validateFields | 校验并获取一组输入域的值与 Error | Function([fieldNames: string[]], [options: object], callback: Function(errors, values)) | | | -| validateFieldsAndScroll | 与 `validateFields` 相似,但校验完后,如果校验不通过的菜单域不在可见范围内,则自动滚动进可见范围 | 参考 `validateFields` | | | -| getFieldError | 获取某个输入控件的 Error | Function(name) | | | -| isFieldValidating | 判断一个输入控件是否在校验状态 | Function(name) | | | -| resetFields | 重置一组输入控件的值与状态,如不传入参数,则重置所有组件 | Function([names: string[]]) | | | -| getFieldProps 详见下面描述 | | | | | - -#### this.props.form.getFieldProps(id, options) - -| 参数 | 说明 | 类型 | 可选值 |默认值 | -|-----------|------------------------------------------|------------|-------|--------| -| options.id | 必填输入控件唯一标志 | string | | | -| options.valuePropName | 子节点的值的属性,如 Checkbox 的是 'checked' | string | | 'value' | -| options.initialValue | 子节点的初始值,类型、可选值均由子节点决定 | | | | -| options.trigger | 收集子节点的值的时机 | string | | 'onChange' | -| options.validateTrigger | 校验子节点值的时机 | string | | 'onChange' | -| options.rules | 校验规则,参见 [async-validator](https://github.com/yiminghe/async-validator) | array | | | | - - -### Form.Item - -| 参数 | 说明 | 类型 | 可选值 |默认值 | -|-----------|------------------------------------------|------------|-------|--------| -| label | label 标签的文本 | string | | | -| labelCol | label 标签布局,通 `` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}` | object | | | -| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | object | | | -| help | 提示信息,如不设置,则会根据校验规则自动生成 | string | | | -| extra | 额外的提示信息,和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。 | string | | | -| required | 是否必填,如不设置,则会根据校验规则自动生成 | bool | | false | -| validateStatus | 校验状态,如不设置,则会根据校验规则自动生成 | string | 'success' 'warning' 'error' 'validating' | | -| hasFeedback | 配合 validateStatus 属性使用,是否展示校验状态图标 | bool | | false | -| prefixCls | 样式类名,默认为 ant-form,通常您不需要设置 | string | | 'ant-form' | - -### Input - -| 参数 | 说明 | 类型 | 可选值 |默认值 | -|-----------|------------------------------------------|------------|-------|--------| -| type | 【必须】声明 input 类型,同原生 input 标签的 type 属性 | string | | 'text' | -| id | id | number 或 string | | | -| value | value 值 | any | | | -| defaultValue | 设置初始默认值 | any | | | -| size | 控件大小,默认值为 default 。注:标准表单内的输入框大小限制为 large。 | string | {'large','default','small'} | 'default' | -| disabled | 是否禁用状态,默认为 false | bool | | false | -| addonBefore | 带标签的 input,设置前置标签 | node | | | -| addonAfter | 带标签的 input,设置后置标签 | node | | | -| prefixCls | 样式类名前缀,默认是 ant,通常您不需要设置 | string | | 'ant' | - -> 如果 `Input` 在 `Form.Item` 内,并且 `Form.Item` 设置了 `id` 和 `options` 属性,则 `value` `defaultValue` 和 `id` 属性会被自动设置。 - -#### Input.Group - -```html - // 样式类名前缀,默认是 ant-input-group,通常您不需要设置。 - {children} - -``` - - diff --git a/components/form/index.tsx b/components/form/index.tsx new file mode 100644 index 0000000000..f1300440e0 --- /dev/null +++ b/components/form/index.tsx @@ -0,0 +1,40 @@ +import React, { PropTypes } from 'react'; +import createDOMForm from 'rc-form/lib/createDOMForm'; +import Form from './Form'; +import FormItem from './FormItem'; +import ValueMixin from './ValueMixin'; +import assign from 'object-assign'; +import { FIELD_META_PROP } from './constants'; + +Form.create = (o = {}) => { + const options = assign({}, o, { + fieldNameProp: 'id', + fieldMetaProp: FIELD_META_PROP, + }); + const formWrapper = createDOMForm(options); + + /* eslint-disable react/prefer-es6-class */ + return (Component) => formWrapper(React.createClass({ + propTypes: { + form: PropTypes.object.isRequired, + }, + childContextTypes: { + form: PropTypes.object.isRequired, + }, + getChildContext() { + return { + form: this.props.form, + }; + }, + render() { + return ; + }, + })); +}; + +Form.Item = FormItem; + +// @Deprecated +Form.ValueMixin = ValueMixin; + +export default Form; diff --git a/components/form/index.zh-CN.md b/components/form/index.zh-CN.md new file mode 100644 index 0000000000..93207469cd --- /dev/null +++ b/components/form/index.zh-CN.md @@ -0,0 +1,124 @@ +--- +category: Components +chinese: 表单 +type: Form Controls +cols: 1 +english: Form +--- + +具有数据收集、校验和提交功能的表单,包含复选框、单选框、输入框、下拉选择框等元素。 + + +## 表单 + +我们为 `form` 提供了以下两种排列方式: + +- 水平排列:可以实现 `label` 标签和表单控件的水平排列; +- 行内排列:使其表现为 `inline-block` 级别的控件。 + +## 表单域 + +表单一定会包含表单域,表单域可以是输入控件,标准表单域,标签,下拉菜单,文本域等。 + +这里我们封装了表单域 `` 。 + +```jsx + + {children} + +``` + +> 注:标准表单中一律使用大号控件。 + +## API + +### Form + +**更多示例参考 [rc-form](http://react-component.github.io/form/)**。 + +| 参数 | 说明 | 类型 | 默认值 | +|-----------|------------------------------------------|------------|-------| +| form | 经 `Form.create()` 包装过的组件会自带 `this.props.form` 属性,直接传给 Form 即可。1.7.0 之后无需设置 | object | 无 | +| vertical | 垂直排列布局 | boolean | false | +| horizontal | 水平排列布局 | boolean | false | +| inline | 行内排列布局 | boolean | false | +| onSubmit | 数据验证成功后回调事件 | Function(e:Event) | | +| prefixCls | 样式类名,默认为 ant-form,通常您不需要设置 | string | 'ant-form' | + +### Form.create(options) + +使用方式如下: + +```jsx +class CustomizedForm extends React.Component {} + +CustomizedForm = Form.create({})(CustomizedForm); +``` + +`options` 的配置项如下。 + +| 参数 | 说明 | 类型 | +|-----------|------------------------------------------|------------| +| onFieldsChange | 当 `Form.Item` 子节点的值发生改变时触发,可以把对应的值转存到 Redux store | Function(props, fields) | +| mapPropsToFields | 把 props 转为对应的值,可用于把 Redux store 中的值读出 | Function(props): Object{ fieldName: Object{ value } } | + +经过 `Form.create` 包装的组件将会自带 `this.props.form` 属性,`this.props.form` 提供的 API 如下: + +| 参数 | 说明 | 类型 | +|-----------|------------------------------------------|------------| +| getFieldsValue | 获取一组输入控件的值,如不传入参数,则获取全部组件的值 | Function([fieldNames: string[]]) | +| getFieldValue | 获取一个输入控件的值 | Function(fieldName: string) | +| setFieldsValue | 设置一组输入控件的值 | Function(obj: object) | +| setFields | 设置一组输入控件的值与 Error | Function(obj: object) | +| validateFields | 校验并获取一组输入域的值与 Error | Function([fieldNames: string[]], [options: object], callback: Function(errors, values)) | +| validateFieldsAndScroll | 与 `validateFields` 相似,但校验完后,如果校验不通过的菜单域不在可见范围内,则自动滚动进可见范围 | 参考 `validateFields` | +| getFieldError | 获取某个输入控件的 Error | Function(name) | +| isFieldValidating | 判断一个输入控件是否在校验状态 | Function(name) | +| resetFields | 重置一组输入控件的值与状态,如不传入参数,则重置所有组件 | Function([names: string[]]) | +| getFieldProps | 用于和表单进行双向绑定,详见下方描述 | | + +### this.props.form.getFieldProps(id, options) + +#### 特别注意 + +如果使用的是 `react@<15.3.0`,则 `getFieldProps` 调用不能位于纯函数组件中: https://github.com/facebook/react/pull/6534 + +`getFieldProps` 返回的属性包括 `id`、`value`(或你设置的其它 `valuePropName`)、`ref`、`onChange`(或者你设置的其它 `trigger` `validateTrigger`),**所以不应再设置同样的属性**,以免冲突。如果对其返回值的细节有兴趣,可以 `console.log` 出来查看。 + +> 在表单中 `defaultValue` 也不应该被设置,请使用下面的 `initialValue`。 + +#### getFieldProps options + +| 参数 | 说明 | 类型 | 默认值 | +|-----------|-----------------------------------------|-----|--------| +| options.id | 必填输入控件唯一标志 | string | | +| options.valuePropName | 子节点的值的属性,如 Switch 的是 'checked' | string | 'value' | +| options.initialValue | 子节点的初始值,类型、可选值均由子节点决定 | | | +| options.trigger | 收集子节点的值的时机 | string | 'onChange' | +| options.getValueFromEvent | 可以把 onChange 的参数转化为控件的值,例如 DatePicker 可设为:`(date, dateString) => dateString` | function(..args) | [reference](https://github.com/react-component/form#optiongetvaluefromevent) | +| options.validateTrigger | 校验子节点值的时机 | string | 'onChange' | +| options.rules | 校验规则,参见 [async-validator](https://github.com/yiminghe/async-validator) | array | | +| options.onXXX | 由于 `getFieldProps` 会占用 `onChange` 等事件(即你所设置的 `trigger` `validateTrigger`),所以如果仍需绑定事件,请在 `options` 内设置 | function | 无 | +| options.exclusive | 是否和其他控件互斥,特别用于 Radio 单选控件 | boolean | false | + +### Form.Item + +> 一个 Form.Item 建议只放一个 child,有多个 child 时,`help` `required` `validateStatus` 无法自动生成。 + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|-----------|------------------------------------------|-----------|-------|--------| +| label | label 标签的文本 | string | | | +| labelCol | label 标签布局,通 `` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}` | object | | | +| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | object | | | +| help | 提示信息,如不设置,则会根据校验规则自动生成 | string | | | +| extra | 额外的提示信息,和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。 | string | | | +| required | 是否必填,如不设置,则会根据校验规则自动生成 | bool | | false | +| validateStatus | 校验状态,如不设置,则会根据校验规则自动生成 | string | 'success' 'warning' 'error' 'validating' | | +| hasFeedback | 配合 validateStatus 属性使用,展示校验状态图标,建议只配合 Input 组件使用 | bool | | false | +| prefixCls | 样式类名,默认为 ant-form,通常您不需要设置 | string | | 'ant-form' | + + diff --git a/style/components/form.less b/components/form/style/index.less similarity index 56% rename from style/components/form.less rename to components/form/style/index.less index 0c7adfc14e..da4b7c8566 100644 --- a/style/components/form.less +++ b/components/form/style/index.less @@ -1,4 +1,11 @@ -@import "../mixins/index"; +@import "../../style/themes/default"; +@import "../../style/mixins/index"; +@import "../../input/style/mixin"; +@import "../../button/style/mixin"; +@import "../../layout/style/mixin"; +@import "./mixin"; + +@form-prefix-cls: ant-form; .reset-form(); @@ -11,7 +18,7 @@ label { } } -.@{css-prefix}form-item-required:before { +.@{form-prefix-cls}-item-required:before { display: inline-block; margin-right: 4px; content: "*"; @@ -20,21 +27,6 @@ label { color: @label-required-color; } -// Input styles -.@{css-prefix}input { - .input; -} - -//== Input type: with extra icon -.has-feedback { - .input-with-icon(); -} - -//== Style for input-group: input with label, with button or dropdown... -.@{css-prefix}input-group { - .input-group(~"@{css-prefix}input"); -} - // Radio && Checkbox input[type="radio"], input[type="checkbox"] { @@ -44,19 +36,21 @@ input[type="checkbox"] { cursor: @cursor-disabled; } } + // These classes are used directly on
); - } + }, }); ReactDOM.render(, mountNode); diff --git a/components/input-number/demo/size.md b/components/input-number/demo/size.md index 553bc68eb2..519c33344c 100644 --- a/components/input-number/demo/size.md +++ b/components/input-number/demo/size.md @@ -1,11 +1,10 @@ -# 三种大小 - -- order: 1 +--- +order: 1 +title: 三种大小 +--- 三种大小的数字输入框,当 size 分别为 `large` 和 `small` 时,输入框高度为 `32px` 和 `22px` ,默认高度为 `28px` ---- - ````jsx import { InputNumber } from 'antd'; @@ -14,16 +13,16 @@ function onChange(value) { } ReactDOM.render( -
- - - -
+
+ + + +
, mountNode); ```` ````css -.ant-input-number{ +.ant-input-number { margin-right: 10px; } ```` diff --git a/components/input-number/index.jsx b/components/input-number/index.jsx deleted file mode 100644 index 7e630a5220..0000000000 --- a/components/input-number/index.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; -import InputNumber from 'rc-input-number'; - -export default React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-input-number', - step: 1, - }; - }, - render() { - const { className, size, ...other } = this.props; - const inputNumberClass = classNames({ - 'ant-input-number-lg': size === 'large', - 'ant-input-number-sm': size === 'small', - [className]: !!className, - }); - - return ; - } -}); diff --git a/components/input-number/index.md b/components/input-number/index.md index b50ff41380..29f7ff1781 100644 --- a/components/input-number/index.md +++ b/components/input-number/index.md @@ -1,9 +1,8 @@ -# InputNumber - -- category: Components -- chinese: 数字输入框 -- type: 表单 - +--- +category: Components +chinese: 数字输入框 +type: Form Controls +english: InputNumber --- 通过鼠标或键盘,输入范围内的数值。 @@ -21,7 +20,7 @@ | min | 最小值 | Number | -Infinity | | max | 最大值 | Number | Infinity | | value | 当前值 | Number | | -| step | 每次改变步数 | Number or String | 1 | +| step | 每次改变步数,可以为小数 | Number or String | 1 | | defaultValue | 初始值 | Number | | | onChange | 变化回调 | Function | | | disabled | 禁用 | Boolean | false | diff --git a/components/input-number/index.tsx b/components/input-number/index.tsx new file mode 100644 index 0000000000..973026df01 --- /dev/null +++ b/components/input-number/index.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import classNames from 'classnames'; +import RcInputNumber from 'rc-input-number'; +import splitObject from '../_util/splitObject'; + +export default class InputNumber extends React.Component { + static defaultProps = { + prefixCls: 'ant-input-number', + step: 1, + }; + + render() { + const [{ className, size }, others] = splitObject(this.props, + ['size', 'className']); + const inputNumberClass = classNames({ + [`${this.props.prefixCls}-lg`]: size === 'large', + [`${this.props.prefixCls}-sm`]: size === 'small', + [className]: !!className, + }); + + return ; + } +} diff --git a/style/components/inputNumber.less b/components/input-number/style/index.less similarity index 96% rename from style/components/inputNumber.less rename to components/input-number/style/index.less index a409249f6d..3d3fc32074 100644 --- a/style/components/inputNumber.less +++ b/components/input-number/style/index.less @@ -1,16 +1,13 @@ -@input-number-prefix-cls: ant-input-number; +@import "../../style/themes/default"; +@import "../../style/mixins/index"; +@import "../../input/style/mixin"; -@import "../mixins/iconfont"; -@import "../mixins/input"; +@input-number-prefix-cls: ant-input-number; .handler-disabled() { opacity: 0.72; color: #ccc !important; - cursor: default; - &:hover { - color: #ccc; - cursor: default; - } + cursor: not-allowed; } .@{input-number-prefix-cls} { diff --git a/components/input-number/style/index.tsx b/components/input-number/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/input-number/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/input/Group.tsx b/components/input/Group.tsx new file mode 100644 index 0000000000..36b7cbdf7b --- /dev/null +++ b/components/input/Group.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import classNames from 'classnames'; + +export default function Group(props) { + const className = classNames({ + 'ant-input-group': true, + 'ant-input-group-lg': props.size === 'large', + 'ant-input-group-sm': props.size === 'small', + [props.className]: !!props.className, + }); + return ( + + {props.children} + + ); +} + +Group.propTypes = { + children: React.PropTypes.any, +}; diff --git a/components/input/Input.tsx b/components/input/Input.tsx new file mode 100644 index 0000000000..91f6fc0e6d --- /dev/null +++ b/components/input/Input.tsx @@ -0,0 +1,195 @@ +import * as React from 'react'; +import { Component, PropTypes } from 'react'; +import classNames from 'classnames'; +import calculateNodeHeight from './calculateNodeHeight'; +import assign from 'object-assign'; +import omit from 'object.omit'; + +function fixControlledValue(value) { + if (typeof value === 'undefined' || value === null) { + return ''; + } + return value; +} + +function onNextFrame(cb) { + if (window.requestAnimationFrame) { + return window.requestAnimationFrame(cb); + } + return window.setTimeout(cb, 1); +} + +function clearNextFrameAction(nextFrameId) { + if (window.cancelAnimationFrame) { + window.cancelAnimationFrame(nextFrameId); + } else { + window.clearTimeout(nextFrameId); + } +} + +export default class Input extends Component { + static defaultProps = { + defaultValue: '', + disabled: false, + prefixCls: 'ant-input', + type: 'text', + onPressEnter() {}, + onKeyDown() {}, + onChange() {}, + autosize: false, + }; + + static propTypes = { + type: PropTypes.string, + id: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + ]), + size: PropTypes.oneOf(['small', 'default', 'large']), + disabled: PropTypes.bool, + value: PropTypes.any, + defaultValue: PropTypes.any, + className: PropTypes.string, + addonBefore: PropTypes.node, + addonAfter: PropTypes.node, + prefixCls: PropTypes.string, + autosize: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), + onPressEnter: PropTypes.func, + onKeyDown: PropTypes.func, + }; + + constructor(props) { + super(props); + this.state = { + textareaStyles: null, + }; + } + + componentDidMount() { + this.resizeTextarea(); + } + + componentWillReceiveProps(nextProps) { + // Re-render with the new content then recalculate the height as required. + if (this.props.value !== nextProps.value) { + if (this.nextFrameActionId) { + clearNextFrameAction(this.nextFrameActionId); + } + this.nextFrameActionId = onNextFrame(this.resizeTextarea); + } + } + + handleKeyDown = (e) => { + if (e.keyCode === 13) { + this.props.onPressEnter(e); + } + this.props.onKeyDown(e); + } + + handleTextareaChange = (e) => { + if (!('value' in this.props)) { + this.resizeTextarea(); + } + this.props.onChange(e); + } + + resizeTextarea = () => { + const { type, autosize } = this.props; + if (type !== 'textarea' || !autosize || !this.refs.input) { + return; + } + const minRows = autosize ? autosize.minRows : null; + const maxRows = autosize ? autosize.maxRows : null; + const textareaStyles = calculateNodeHeight(this.refs.input, false, minRows, maxRows); + this.setState({ textareaStyles }); + } + + renderLabledInput(children) { + const props = this.props; + const wrapperClassName = `${props.prefixCls}-group`; + const addonClassName = `${wrapperClassName}-addon`; + const addonBefore = props.addonBefore ? ( + + {props.addonBefore} + + ) : null; + + const addonAfter = props.addonAfter ? ( + + {props.addonAfter} + + ) : null; + + const className = classNames({ + [`${props.prefixCls}-wrapper`]: true, + [wrapperClassName]: (addonBefore || addonAfter), + }); + + return ( + + {addonBefore} + {children} + {addonAfter} + + ); + } + + renderInput() { + const props = assign({}, this.props); + + // Fix https://fb.me/react-unknown-prop + const otherProps = omit(this.props, [ + 'prefixCls', + 'onPressEnter', + 'autosize', + 'addonBefore', + 'addonAfter', + ]); + + const prefixCls = props.prefixCls; + if (!props.type) { + return props.children; + } + + const inputClassName = classNames({ + [prefixCls]: true, + [`${prefixCls}-sm`]: props.size === 'small', + [`${prefixCls}-lg`]: props.size === 'large', + [props.className]: !!props.className, + }); + + if ('value' in props) { + otherProps.value = fixControlledValue(props.value); + // Input elements must be either controlled or uncontrolled, + // specify either the value prop, or the defaultValue prop, but not both. + delete otherProps.defaultValue; + } + + switch (props.type) { + case 'textarea': + return ( + -

随便写点什么

- - , -
-
- -
-
, -
-
- -
-
+ +

大眼萌 minion

+
, + + + , + + + 男的 + 女的 + + , + + + , + + + + + , ] : null} ); - } + }, }); ReactDOM.render(, mountNode); diff --git a/components/queue-anim/demo/page.md b/components/queue-anim/demo/page.md index ac06146c50..6ace0d5ad1 100644 --- a/components/queue-anim/demo/page.md +++ b/components/queue-anim/demo/page.md @@ -1,77 +1,75 @@ -# 一个复杂些的例子 - -- order: 6 +--- +order: 6 +title: 一个复杂些的例子 +--- 模拟一个完整的页面。 ---- - ````jsx import { QueueAnim, Button } from 'antd'; const Test = React.createClass({ getInitialState() { return { - show: true + show: true, }; }, onClick() { this.setState({ - show: !this.state.show + show: !this.state.show, }); }, render() { - return ( -
-

- -

- - {this.state.show ? [ -
-
- - logo -
- + const page = this.state.show ? [ +
+
+ logo + logo +
+ +
  • +
  • +
  • +
  • +
  • +
    +
    , + +
    我是标题
    +
    + +
  • +
  • +
  • +
    +
    +
    我是标题
    +
    + +
    +
  • -
    , - -
    我是标题
    -
    - -
  • -
  • -
  • -
    -
    -
    我是标题
    -
    - -
    - -
  • -
  • -
  • -
  • -
  • -
    -
    -
    -
    , - -
    - ] : null} -
    +
    +
    , + +
    +
    , + ] : null; + return ( +
    +

    + +

    + {page}
    ); - } + }, }); ReactDOM.render(, mountNode); diff --git a/components/queue-anim/demo/router.md b/components/queue-anim/demo/router.md index 292f736f75..6e4379d14f 100644 --- a/components/queue-anim/demo/router.md +++ b/components/queue-anim/demo/router.md @@ -1,124 +1,116 @@ -# Router 默认进出场 - -- order: 7 +--- +order: 7 +iframe: true +title: Router 默认进出场 +--- router 组合的进场与出场动画。 ---- - ````jsx const ReactRouter = require('react-router'); let { Router, Route, Link, hashHistory } = ReactRouter; import { QueueAnim, Menu } from 'antd'; -const App = React.createClass({ - render() { - const key = this.props.location.pathname; - const keys = key.replace('/', '') ? [key.replace('/', '')] : ['home']; - return ( -
    - - - 首页 - - - Page 1 - - - Page 2 - - - - {React.cloneElement(this.props.children || , { key })} - -
    - ); - } -}); +function App(props) { + const key = props.location.pathname; + const keys = key.replace('/', '') ? [key.replace('/', '')] : ['home']; + return ( +
    + + + 首页 + + + Page 1 + + + Page 2 + + + + {React.cloneElement(props.children || , { key })} + +
    + ); +} -const Home = React.createClass({ - render() { - return ( -
    - -
    - -
  • -
  • -
  • -
    -
    -
    - -
  • -
  • -
  • -
    -
    -
    - -
  • -
  • -
  • -
    -
    -
    -
    - ); - } -}); +function Home() { + return ( +
    + +
    + +
  • +
  • +
  • +
    +
    +
    + +
  • +
  • +
  • +
    +
    +
    + +
  • +
  • +
  • +
    +
    +
    +
    + ); +} -const Page1 = React.createClass({ - render() { - return ( -
    - -
    - +function Page1() { + return ( +
    + +
    + +
  • +
  • +
  • +
    +
    +
    + +
    +
  • -
    -
    - -
    - -
  • -
  • -
  • -
    -
    -
    -
    -
    - ); - } -}); +
    +
    +
    +
    + ); +} -const Page2 = React.createClass({ - render() { - return ( -
    -
    -
    - -
    - -
  • -
  • -
  • -
  • -
  • -
  • -
    +function Page2() { + return ( +
    +
    +
    + +
    + +
  • +
  • +
  • +
  • +
  • +
  • -
    +
    - ); - } -}); +
    + ); +} ReactDOM.render(( @@ -131,6 +123,9 @@ ReactDOM.render(( ```` ````css +#components-queue-anim-demo-router iframe { + height: 260px; +} #components-queue-anim-demo-router .demo-router-wrap { position: relative; width: 100%; diff --git a/components/queue-anim/demo/simple.md b/components/queue-anim/demo/simple.md index c278890fee..7c79f6fd91 100644 --- a/components/queue-anim/demo/simple.md +++ b/components/queue-anim/demo/simple.md @@ -1,16 +1,15 @@ -# 默认 - -- order: 0 +--- +order: 0 +title: 默认 +--- 最简单的进场例子。 ---- - ````jsx import { QueueAnim } from 'antd'; ReactDOM.render( - +
    依次进场
    依次进场
    依次进场
    diff --git a/components/queue-anim/index.jsx b/components/queue-anim/index.jsx deleted file mode 100644 index d94d1c4ea3..0000000000 --- a/components/queue-anim/index.jsx +++ /dev/null @@ -1,3 +0,0 @@ -import QueueAnim from 'rc-queue-anim'; - -export default QueueAnim; diff --git a/components/queue-anim/index.md b/components/queue-anim/index.md index 18911d2c3a..aaa559d4ec 100644 --- a/components/queue-anim/index.md +++ b/components/queue-anim/index.md @@ -1,8 +1,8 @@ -# QueueAnim - -- category: Components -- chinese: 进出场动画 - +--- +category: Components +chinese: 进出场动画 +type: Other +english: QueueAnim --- 通过简单的配置对一组元素添加串行的进场动画效果。 @@ -10,15 +10,15 @@ ## 何时使用 - 从内容A到内容B的转变过程时能有效的吸引用户注意力,突出视觉中心,提高整体视觉效果。 - - 小的信息元素排布或块状较多的情况下,根据一定的路径层次依次进场,区分维度层级,来凸显量级,使页面转场更加流畅和舒适,提高整体视觉效果和产品的质感。 - - 特别适合首页和需要视觉展示效果的宣传页,以及单页应用的切换页面动效。 ## API -> 此组件取代了 0.9.x 版本的 [enter-animation](http://09x.ant.design/components/enter-animation/)。 +> 此组件 `antd@1.0.0+` 后标记为废弃,您可以直接使用 `import QueueAnim from 'rc-queue-anim'` 来代替,相关文档也已移到 [ant-motioin](http://motion.ant.design/components/queue-anim)。 + +> [ant-motioin](http://motion.ant.design/) 是一个动效设计语言,欢迎关注。 元素依次进场。 @@ -46,159 +46,3 @@ | component | string | `div` | QueueAnim 替换的标签名 | > 当以上数据类型为 Array 时,`['left', 'top']` 第一个为进场动画属性, 第二个为离场属性。 - - diff --git a/components/queue-anim/index.tsx b/components/queue-anim/index.tsx new file mode 100644 index 0000000000..65340f2f85 --- /dev/null +++ b/components/queue-anim/index.tsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +import RcQueueAnim from 'rc-queue-anim'; +import warning from 'warning'; + +export default class QueueAnim extends React.Component { + componentDidMount() { + warning(false, '`QueueAnim` is deprecated, ' + + 'you can import QueueAnim from \'rc-queue-anim\' directly.' + + 'The Demo will be moved to http://motion.ant.design/component/queue-anim'); + } + render() { + return ; + } +} diff --git a/components/queue-anim/style/index.tsx b/components/queue-anim/style/index.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/radio/demo/basic.md b/components/radio/demo/basic.md index e244cee35c..ac6753cc1d 100644 --- a/components/radio/demo/basic.md +++ b/components/radio/demo/basic.md @@ -1,14 +1,21 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- -- order: 0 +## zh-CN 最简单的用法。 ---- +## en-US -````jsx +The simplest use. + +```jsx import { Radio } from 'antd'; -ReactDOM.render(Radio -, mountNode); -```` +ReactDOM.render(Radio, mountNode); +``` +2 diff --git a/components/radio/demo/disable.md b/components/radio/demo/disable.md index 9dbaaee04f..f7a4ddb589 100644 --- a/components/radio/demo/disable.md +++ b/components/radio/demo/disable.md @@ -1,23 +1,30 @@ -# 不可用 +--- +order: 1 +title: + zh-CN: 不可用 + en-US: disabled +--- -- order: 1 +## zh-CN Radio 不可用。 ---- +## en-US -````jsx +Radio unavailable. + +```jsx import { Radio, Button } from 'antd'; const App = React.createClass({ getInitialState() { return { - disabled: true + disabled: true, }; }, toggleDisabled() { this.setState({ - disabled: !this.state.disabled + disabled: !this.state.disabled, }); }, render() { @@ -33,8 +40,8 @@ const App = React.createClass({
    ); - } + }, }); ReactDOM.render(, mountNode); -```` +``` diff --git a/components/radio/demo/radiobutton.md b/components/radio/demo/radiobutton.md index 8b497cc02a..bfebc832ec 100644 --- a/components/radio/demo/radiobutton.md +++ b/components/radio/demo/radiobutton.md @@ -1,12 +1,19 @@ -# 按钮样式 +--- +order: 3 +title: + zh-CN: 按钮样式 + en-US: raido style +------------------ -- order: 3 +## zh-CN 按钮样式的单选组合。 ---- +## en-US -````jsx +The combination of radio button style. + +```jsx import { Radio } from 'antd'; const RadioButton = Radio.Button; const RadioGroup = Radio.Group; @@ -41,4 +48,4 @@ ReactDOM.render(
    , mountNode); -```` +``` diff --git a/components/radio/demo/radiogroup-more.md b/components/radio/demo/radiogroup-more.md index e4d5b3f849..7d58053823 100644 --- a/components/radio/demo/radiogroup-more.md +++ b/components/radio/demo/radiogroup-more.md @@ -1,12 +1,19 @@ -# RadioGroup 垂直 +--- +order: 2 +title: + zh-CN: RadioGroup 垂直 + en-US: Vertical RadioGroup +-------------------------- -- order: 2 +## zh-CN 垂直的 RadioGroup,配合更多输入框选项。 ---- +## en-US -````jsx +Vertical RadioGroup, with more radios. + +```jsx import { Radio, Input } from 'antd'; const RadioGroup = Radio.Group; @@ -39,8 +46,8 @@ const App = React.createClass({ ); - } + }, }); ReactDOM.render(, mountNode); -```` +``` diff --git a/components/radio/demo/radiogroup.md b/components/radio/demo/radiogroup.md index c94ec67f88..2c44833bb1 100644 --- a/components/radio/demo/radiogroup.md +++ b/components/radio/demo/radiogroup.md @@ -1,12 +1,19 @@ -# RadioGroup 组合 +--- +order: 1 +title: + zh-CN: RadioGroup 组合 + en-US: RadioGroup group +----------------------- -- order: 1 +## zh-CN 一组互斥的 Radio 配合使用。 ---- +## en-US -````jsx +A set of mutually exclusive Radio with the use of + +```jsx import { Radio } from 'antd'; const RadioGroup = Radio.Group; @@ -28,11 +35,11 @@ const App = React.createClass({ A B C - D + D ); - } + }, }); ReactDOM.render(, mountNode); -```` +``` diff --git a/components/radio/demo/size.md b/components/radio/demo/size.md index 6d94cc3f1e..0dcab55ef8 100644 --- a/components/radio/demo/size.md +++ b/components/radio/demo/size.md @@ -1,12 +1,19 @@ -# 大小 +--- +order: 5 +title: + zh-CN: 大小 + en-US: Size +----------- -- order: 5 +## zh-CN 大中小三种组合,可以和表单输入框进行对应配合。 ---- +## en-US -````jsx +There are three kinds of combination, large medium and small. It can coordinate with input box. + +```jsx import { Radio } from 'antd'; const RadioButton = Radio.Button; const RadioGroup = Radio.Group; @@ -37,4 +44,4 @@ ReactDOM.render(
    , mountNode); -```` +``` diff --git a/components/radio/group.jsx b/components/radio/group.tsx similarity index 59% rename from components/radio/group.jsx rename to components/radio/group.tsx index 71a8ace2ca..a93058141e 100644 --- a/components/radio/group.jsx +++ b/components/radio/group.tsx @@ -1,12 +1,13 @@ -import React from 'react'; +import * as React from 'react'; import classNames from 'classnames'; import Radio from './radio'; import RadioButton from './radioButton'; - +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import assign from 'object-assign'; function getCheckedValue(children) { let value = null; let matched = false; - React.Children.forEach(children, (radio) => { + React.Children.forEach(children, (radio: any) => { if (radio && radio.props && radio.props.checked) { value = radio.props.value; matched = true; @@ -15,17 +16,29 @@ function getCheckedValue(children) { return matched ? { value } : undefined; } -export default React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-radio-group', - disabled: false, - onChange() { - }, - }; - }, - getInitialState() { - let props = this.props; +export interface RadioGroupProps { + /** 选项变化时的回调函数*/ + onChange?: React.FormEventHandler; + /** 用于设置当前选中的值*/ + value?: string | number; + /** 默认选中的值*/ + defaultValue?: string | number; + /** 大小,只对按钮样式生效*/ + size?: 'large' | 'default' | 'small'; + style?: React.CSSProperties; + prefixCls?: string; + disabled?: boolean; +} + +export default class RadioGroup extends React.Component { + static defaultProps = { + prefixCls: 'ant-radio-group', + disabled: false, + onChange() { + }, + }; + constructor(props) { + super(props); let value; if ('value' in props) { value = props.value; @@ -35,10 +48,10 @@ export default React.createClass({ const checkedValue = getCheckedValue(props.children); value = checkedValue && checkedValue.value; } - return { + this.state = { value, }; - }, + } componentWillReceiveProps(nextProps) { if ('value' in nextProps) { this.setState({ @@ -52,30 +65,31 @@ export default React.createClass({ }); } } - }, - onRadioChange(ev) { + } + shouldComponentUpdate(...args) { + return PureRenderMixin.shouldComponentUpdate.apply(this, args); + } + onRadioChange = (ev) => { if (!('value' in this.props)) { this.setState({ value: ev.target.value, }); } this.props.onChange(ev); - }, + } render() { const props = this.props; - const children = React.Children.map(props.children, (radio) => { + const children = React.Children.map(props.children, (radio: any) => { if (radio && (radio.type === Radio || radio.type === RadioButton) && radio.props) { const keyProps = {}; if (!('key' in radio) && typeof radio.props.value === 'string') { - keyProps.key = radio.props.value; + (keyProps as any).key = radio.props.value; } - return React.cloneElement(radio, { - ...keyProps, - ...radio.props, + return React.cloneElement(radio, assign({}, keyProps, radio.props, { onChange: this.onRadioChange, checked: this.state.value === radio.props.value, disabled: radio.props.disabled || this.props.disabled, - }); + })); } return radio; }); @@ -84,5 +98,5 @@ export default React.createClass({ [`${props.prefixCls}-${props.size}`]: props.size, }); return
    {children}
    ; - }, -}); + } +} diff --git a/components/radio/index.en-US.md b/components/radio/index.en-US.md new file mode 100644 index 0000000000..3efdafa27f --- /dev/null +++ b/components/radio/index.en-US.md @@ -0,0 +1,34 @@ +--- +category: Components +type: Form Controls +title: Radio +--- + +Radio. + +## When to use + +- Used to select a single state in multiple options. +- The different between Select, Radio is visbile to user, it can facilitate users in the comparison of choice. So, when you want to use Radio, option should not be too much. + + +## API + +### Radio + +| Property | Description | Type | optional | Default | +|----------------|------------------------------------------|------------|---------|--------| +| checked | Specifies whether the radio is selected. | Boolean | false | +| defaultChecked | Specifies the initial state: whether or not the radio is selected. | Boolean | false | +| value | According to value for comparison, to determine whether the selected | String | | none | + +### RadioGroup + +radio group,wrap a group of `Radio`。 + +| Property | Description | Type | optional | Default | +|----------------|----------------------------------|-------------------|--------|--------| +| onChange | The callback function that is triggered when the state changes. | Function(e:Event) | none | none | +| value | Used for setting the currently selected value. | String | none | none | +| defaultValue | Default selected value | String | none | none | +| size | Size, only on radio style | String | `large` `default` `small` | `default` | diff --git a/components/radio/index.jsx b/components/radio/index.jsx deleted file mode 100644 index 70810c5454..0000000000 --- a/components/radio/index.jsx +++ /dev/null @@ -1,7 +0,0 @@ -import AntRadio from './radio'; -import Group from './group'; -import Button from './radioButton'; - -AntRadio.Button = Button; -AntRadio.Group = Group; -export default AntRadio; diff --git a/components/radio/index.tsx b/components/radio/index.tsx new file mode 100644 index 0000000000..334e01818a --- /dev/null +++ b/components/radio/index.tsx @@ -0,0 +1,7 @@ +import Radio from './radio'; +import Group from './group'; +import Button from './radioButton'; + +Radio.Button = Button; +Radio.Group = Group; +export default Radio; diff --git a/components/radio/index.md b/components/radio/index.zh-CN.md similarity index 94% rename from components/radio/index.md rename to components/radio/index.zh-CN.md index 35e367f692..5fed5639a9 100644 --- a/components/radio/index.md +++ b/components/radio/index.zh-CN.md @@ -1,9 +1,8 @@ -# Radio - -- category: Components -- chinese: 单选框 -- type: 表单 - +--- +category: Components +subtitle: 单选框 +type: Form Controls +title: Radio --- 单选框。 diff --git a/components/radio/radio.jsx b/components/radio/radio.jsx deleted file mode 100644 index 7c5ccfd202..0000000000 --- a/components/radio/radio.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import Radio from 'rc-radio'; -import React from 'react'; -import classNames from 'classnames'; - -const AntRadio = React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-radio' - }; - }, - render() { - const { prefixCls, children, checked, disabled, className, style } = this.props; - const classString = classNames({ - [prefixCls]: true, - [`${prefixCls}-checked`]: checked, - [`${prefixCls}-disabled`]: disabled, - [className]: !!className, - }); - return ( - - ); - } -}); - -export default AntRadio; diff --git a/components/radio/radio.tsx b/components/radio/radio.tsx new file mode 100644 index 0000000000..b9f3cd3553 --- /dev/null +++ b/components/radio/radio.tsx @@ -0,0 +1,50 @@ +import RcRadio from 'rc-radio'; +import * as React from 'react'; +import classNames from 'classnames'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; + +export interface RadioProps { + /** 指定当前是否选中*/ + checked?: boolean; + /** 初始是否选中*/ + defaultChecked?: boolean; + /** 根据 value 进行比较,判断是否选中 */ + value?: string | number; + style?: React.CSSProperties; + prefixCls?: string; + disabled?: boolean; + className?: string; + onChange?: (e: any) => any; +} + +export default class Radio extends React.Component { + static Group: any; + static Button: any; + + static defaultProps = { + prefixCls: 'ant-radio', + }; + shouldComponentUpdate(...args) { + return PureRenderMixin.shouldComponentUpdate.apply(this, args); + } + render() { + const { prefixCls, children, checked, disabled, className, style } = this.props; + const wrapperClassString = classNames({ + [`${prefixCls}-wrapper`]: true, + [`${prefixCls}-wrapper-checked`]: checked, + [`${prefixCls}-wrapper-disabled`]: disabled, + [className]: !!className, + }); + const classString = classNames({ + [`${prefixCls}`]: true, + [`${prefixCls}-checked`]: checked, + [`${prefixCls}-disabled`]: disabled, + }); + return ( + + ); + } +} diff --git a/components/radio/radioButton.jsx b/components/radio/radioButton.jsx deleted file mode 100644 index ea3f0fe906..0000000000 --- a/components/radio/radioButton.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import AntRadio from './radio'; - -const RadioButton = React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-radio-button', - }; - }, - render() { - return ( - - ); - } -}); - -export default RadioButton; diff --git a/components/radio/radioButton.tsx b/components/radio/radioButton.tsx new file mode 100644 index 0000000000..e924f4c020 --- /dev/null +++ b/components/radio/radioButton.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import Radio from './radio'; + +export interface RadioButtonProps { + value: string | number; + style?: React.CSSProperties; +} + +export default class RadioButton extends React.Component { + static defaultProps = { + prefixCls: 'ant-radio-button', + }; + render() { + return ( + + ); + } +} diff --git a/components/radio/style/index.less b/components/radio/style/index.less new file mode 100644 index 0000000000..7859363af0 --- /dev/null +++ b/components/radio/style/index.less @@ -0,0 +1,236 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; + +@radio-prefix-cls: ant-radio; +@radio-group-prefix-cls: ~"@{radio-prefix-cls}-group"; +@radio-inner-prefix-cls: ~"@{radio-prefix-cls}-inner"; +@radio-duration: .2s; + +.@{radio-group-prefix-cls} { + display: inline-block; + font-size: @font-size-base; +} + +// 一般状态 +.@{radio-prefix-cls}-wrapper { + font-size: @font-size-base; + vertical-align: middle; + display: inline-block; + position: relative; + white-space: nowrap; + margin-right: 8px; +} + +.@{radio-prefix-cls} { + white-space: nowrap; + outline: none; + display: inline-block; + position: relative; + line-height: 1; + vertical-align: middle; + cursor: pointer; + &:hover, + &-focused { + .@{radio-inner-prefix-cls} { + border-color: #bcbcbc; + } + } + &-inner { + &:after { + position: absolute; + width: 6px; + height: 6px; + left: 3px; + top: 3px; + border-radius: @border-radius-base; + display: table; + border-top: 0; + border-left: 0; + content: ' '; + background-color: @primary-color; + opacity: 0; + transform: scale(0); + transition: all @radio-duration @ease-in-out-circ; + } + + position: relative; + top: 0; + left: 0; + display: inline-block; + width: 14px; + height: 14px; + border-width: 1px; + border-style: solid; + border-radius: 14px; + border-color: @border-color-base; + background-color: #fff; + transition: all @radio-duration @ease-in-out-circ; + } + + &-input { + position: absolute; + left: 0; + z-index: 1; + cursor: pointer; + opacity: 0; + top: 0; + bottom: 0; + right: 0; + } +} + +// 选中状态 +.@{radio-prefix-cls}-checked { + .@{radio-inner-prefix-cls} { + border-color: @border-color-base; + &:after { + transform: scale(1); + opacity: 1; + transition: all @radio-duration @ease-in-out-circ; + } + } +} + +.@{radio-prefix-cls}-disabled { + &:hover { + .@{radio-inner-prefix-cls} { + border-color: @border-color-base; + } + } + .@{radio-inner-prefix-cls} { + border-color: @border-color-base; + background-color: #f3f3f3; + &:after { + background-color: #cccccc; + } + } + + .@{radio-inner-prefix-cls}-input { + cursor: default; + } + + .@{radio-prefix-cls}-disabled + span { + color: #ccc; + cursor: @cursor-disabled; + } +} + +span.@{radio-prefix-cls} + * { + margin-left: 8px; + margin-right: 8px; +} + +.@{radio-prefix-cls}-button-wrapper { + margin: 0; + height: 28px; + line-height: 26px; + color: @btn-default-color; + display: inline-block; + transition: all 0.3s ease; + cursor: pointer; + border: 1px solid @border-color-base; + border-left: 0; + background: #fff; + padding: 0 16px; + + a { + color: @btn-default-color; + } + + > .@{radio-prefix-cls}-button { + margin-left: 0; + display: block; + width: 0; + height: 0; + } + + .@{radio-group-prefix-cls}-large & { + height: 32px; + line-height: 30px; + } + + .@{radio-group-prefix-cls}-small & { + height: 22px; + line-height: 20px; + padding: 0 12px; + &:first-child { + border-radius: @border-radius-sm 0 0 @border-radius-sm; + } + &:last-child { + border-radius: 0 @border-radius-sm @border-radius-sm 0; + } + } + + &:first-child { + border-radius: @border-radius-base 0 0 @border-radius-base; + border-left: 1px solid @border-color-base; + } + + &:last-child { + border-radius: 0 @border-radius-base @border-radius-base 0; + } + + &:first-child:last-child { + border-radius: @border-radius-base; + } + + &:hover, + &-focused { + color: @primary-color; + position: relative; + } + + .@{radio-prefix-cls}-inner, + input { + .opacity(0); + width: 0; + height: 0; + } + + &-checked { + background: #fff; + border-color: @primary-color; + color: @primary-color; + box-shadow: -1px 0 0 0 @primary-color; + + &:first-child { + border-color: @primary-color; + box-shadow: none!important; + } + + &:hover { + border-color: tint(@primary-color, 20%); + box-shadow: -1px 0 0 0 tint(@primary-color, 20%); + color: tint(@primary-color, 20%); + } + + &:active { + border-color: shade(@primary-color, 5%); + box-shadow: -1px 0 0 0 shade(@primary-color, 5%); + color: shade(@primary-color, 5%); + } + } + + &-disabled { + border-color: @border-color-base; + background-color: @background-color-base; + cursor: not-allowed; + color: #ccc; + + &:first-child, + &:hover { + border-color: @border-color-base; + background-color: @background-color-base; + color: #ccc; + } + &:first-child { + border-left-color: @border-color-base; + } + } + + &-disabled&-checked { + color: #fff; + background-color: #e6e6e6; + border-color: @border-color-base; + } +} diff --git a/components/radio/style/index.tsx b/components/radio/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/radio/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/rate/demo/basic.md b/components/rate/demo/basic.md new file mode 100644 index 0000000000..7b41ee340d --- /dev/null +++ b/components/rate/demo/basic.md @@ -0,0 +1,20 @@ +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- + +## zh-CN + +最简单的用法。 + +## en-US + +The simplest usage. + +````jsx +import { Rate } from 'antd'; + +ReactDOM.render(, mountNode); +```` diff --git a/components/rate/demo/disabled.md b/components/rate/demo/disabled.md new file mode 100644 index 0000000000..bc060d0cf8 --- /dev/null +++ b/components/rate/demo/disabled.md @@ -0,0 +1,20 @@ +--- +order: 3 +title: + zh-CN: 只读 + en-US: Read only +--- + +## zh-CN + +只读,无法进行鼠标交互。 + +## en-US + +Read only, can't use mouse to interact. + +````jsx +import { Rate } from 'antd'; + +ReactDOM.render(, mountNode); +```` diff --git a/components/rate/demo/half.md b/components/rate/demo/half.md new file mode 100644 index 0000000000..b3b1ea528a --- /dev/null +++ b/components/rate/demo/half.md @@ -0,0 +1,20 @@ +--- +order: 1 +title: + zh-CN: 半星 + en-US: Half star +--- + +## zh-CN + +支持选中半星。 + +## en-US + +Support select half star. + +````jsx +import { Rate } from 'antd'; + +ReactDOM.render(, mountNode); +```` diff --git a/components/rate/demo/text.md b/components/rate/demo/text.md new file mode 100644 index 0000000000..6888089966 --- /dev/null +++ b/components/rate/demo/text.md @@ -0,0 +1,41 @@ +--- +order: 2 +title: + zh-CN: 文案展现 + en-US: Show copywriting +--- + +## zh-CN + +给评分组件加上文案展示。 + +## en-US + +Add copywriting in rate components. + +````jsx +import { Rate } from 'antd'; + +const Rater = React.createClass({ + getInitialState() { + return { + value: 3, + count: null, + }; + }, + handleChange(value) { + this.setState({ value }); + }, + render() { + const { value } = this.state; + return ( + + + {value && {value} 星} + + ); + }, +}); + +ReactDOM.render(, mountNode); +```` diff --git a/components/rate/index.en-US.md b/components/rate/index.en-US.md new file mode 100644 index 0000000000..24bcfdd136 --- /dev/null +++ b/components/rate/index.en-US.md @@ -0,0 +1,23 @@ +--- +category: Components +type: Form Controls +title: Rate +--- + +Rate component. + +## When to use + +- Show evalutate. +- A quick rating operation on something. + +## API + +| Property | Description | type | Default | +|------------|----------------|-------------------|-------------| +| count | star count | Number | 5 | +| value | current value | Number | - | +| defaultValue | default value | Number | 0 | +| onChange(value: Number) | callback | Function | - | +| allowHalf | weather to allow semi selection | Boolean | false | +| disabled | read only, unable to interact | Boolean | false | diff --git a/components/rate/index.tsx b/components/rate/index.tsx new file mode 100644 index 0000000000..f0b269e39e --- /dev/null +++ b/components/rate/index.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { PropTypes } from 'react'; +import RcRate from 'rc-rate'; + +export interface RateProps { + prefixCls?: string; + count?: number; + value?: number; + defaultValue?: number; + allowHalf?: boolean; + disabled?: boolean; + onChange?: (value: number) => any; +} + +export default class Rate extends React.Component { + static propTypes = { + prefixCls: PropTypes.string, + }; + static defaultProps = { + prefixCls: 'ant-rate', + }; + render() { + return ; + } +} diff --git a/components/rate/index.zh-CN.md b/components/rate/index.zh-CN.md new file mode 100644 index 0000000000..7bcb7efa9b --- /dev/null +++ b/components/rate/index.zh-CN.md @@ -0,0 +1,24 @@ +--- +category: Components +subtitle: 评分 +type: Form Controls +title: Rate +--- + +评分组件。 + +## 何时使用 + +- 对评价进行展示。 +- 对事物进行快速的评级操作。 + +## API + +| 属性 | 说明 | 类型 | 默认值 | +|------------|----------------|-------------------|-------------| +| count | star 总数 | Number | 5 | +| value | 当前数,受控值 | Number | - | +| defaultValue | 默认值 | Number | 0 | +| onChange(value: Number) | 回调 | Function | - | +| allowHalf | 是否允许半选 | Boolean | false | +| disabled | 只读,无法进行交互 | Boolean | false | diff --git a/components/rate/style/index.less b/components/rate/style/index.less new file mode 100644 index 0000000000..1d30395c25 --- /dev/null +++ b/components/rate/style/index.less @@ -0,0 +1,78 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; + +@rate-prefix-cls: ant-rate; +@rate-star-color: #f5a623; + +.@{rate-prefix-cls} { + margin: 0; + padding: 0; + list-style: none; + font-size: 18px; + display: inline-block; + vertical-align: middle; + font-family: 'anticon'; + font-weight: normal; + font-style: normal; + + &-disabled &-star { + &:before, + &-content:before { + cursor: default; + } + &:hover { + transform: scale(1); + } + } + + &-star { + margin: 0; + padding: 0; + display: inline-block; + margin-right: 8px; + position: relative; + transition: all 0.3s ease; + + &:hover { + transform: scale(1.1); + } + + &:before, + &-content:before { + color: #e9e9e9; + cursor: pointer; + content: "\E694"; + transition: all 0.3s ease; + display: block; + } + + &-content { + position: absolute; + left: 0; + top: 0; + width: 50%; + height: 100%; + overflow: hidden; + &:before { + color: transparent; + } + } + + &-half &-content:before, + &-full:before { + color: @rate-star-color; + } + + &-half:hover &-content:before, + &-full:hover:before { + color: tint(@rate-star-color, 20%); + } + } + + &-text { + margin-left: 8px; + vertical-align: middle; + display: inline-block; + font-size: @font-size-base; + } +} diff --git a/components/rate/style/index.tsx b/components/rate/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/rate/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/row/index.js b/components/row/index.tsx similarity index 100% rename from components/row/index.js rename to components/row/index.tsx diff --git a/components/row/style/index.tsx b/components/row/style/index.tsx new file mode 100644 index 0000000000..875df4fa55 --- /dev/null +++ b/components/row/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import '../../layout/style/index.less'; diff --git a/components/select/demo/basic.md b/components/select/demo/basic.md index c0cca70948..a39f31b306 100644 --- a/components/select/demo/basic.md +++ b/components/select/demo/basic.md @@ -1,10 +1,17 @@ -# 基本使用 +--- +order: 0 +title: + zh-CN: 基本使用 + en-US: Basic Usage +--- -- order: 0 +## zh-CN 基本使用。 ---- +## en-US + +Basic Usage. ````jsx import { Select } from 'antd'; @@ -22,7 +29,7 @@ ReactDOM.render( - diff --git a/components/select/demo/combobox.md b/components/select/demo/combobox.md index a59a8112c1..e3f3a93110 100644 --- a/components/select/demo/combobox.md +++ b/components/select/demo/combobox.md @@ -1,10 +1,21 @@ -# 智能提示 +--- +order: 4 +title: + zh-CN: 智能提示 + en-US: Automatic completion +--- -- order: 4 +## zh-CN 输入框自动完成功能,下面是一个账号注册表单的例子。 ---- +推荐使用 [AutoComplete](/components/auto-complete) 组件。 + +## en-US + +Automatic completion of select input. + +Using the [AutoComplete](/components/auto-complete) component is strongly recommended instead as it is more flexible and capable. ````jsx @@ -14,7 +25,7 @@ const Option = Select.Option; const Test = React.createClass({ getInitialState() { return { - options: [] + options: [], }; }, handleChange(value) { @@ -36,11 +47,12 @@ const Test = React.createClass({ style={{ width: 200 }} onChange={this.handleChange} filterOption={false} - placeholder="请输入账户名"> + placeholder="请输入账户名" + > {this.state.options} ); - } + }, }); ReactDOM.render(, mountNode); diff --git a/components/select/demo/coordinate.md b/components/select/demo/coordinate.md index 4ab7efbaeb..2402b6462e 100644 --- a/components/select/demo/coordinate.md +++ b/components/select/demo/coordinate.md @@ -1,12 +1,22 @@ -# 联动 +--- +order: 6 +title: + zh-CN: 联动 + en-US: coordinate +--- -- order: 6 +## zh-CN 省市联动是典型的例子。 -推荐使用 [cascader](/components/cascader/) 组件。 +推荐使用 [Cascader](/components/cascader) 组件。 + +## en-US + +Coordinating the selection of provinces and cities is a common use case and demonstrates how selection can be coordinated. + +Using the [Cascader](/components/cascader) component is strongly recommended instead as it is more flexible and capable. ---- ````jsx import { Select } from 'antd'; @@ -15,26 +25,25 @@ const Option = Select.Option; const provinceData = ['浙江', '江苏']; const cityData = { 浙江: ['杭州', '宁波', '温州'], - 江苏: ['南京', '苏州', '镇江'] + 江苏: ['南京', '苏州', '镇江'], }; - const App = React.createClass({ getInitialState() { return { cities: cityData[provinceData[0]], - secondCity: cityData[provinceData[0]][0] + secondCity: cityData[provinceData[0]][0], }; }, handleProvinceChange(value) { this.setState({ cities: cityData[value], - secondCity: cityData[value][0] + secondCity: cityData[value][0], }); }, onSecondCityChange(value) { this.setState({ - secondCity: value + secondCity: value, }); }, render() { @@ -50,7 +59,7 @@ const App = React.createClass({ ); - } + }, }); ReactDOM.render(, mountNode); ```` diff --git a/components/select/demo/label-in-value.md b/components/select/demo/label-in-value.md new file mode 100644 index 0000000000..1cea9d39c3 --- /dev/null +++ b/components/select/demo/label-in-value.md @@ -0,0 +1,36 @@ +--- +order: 10 +title: + zh-CN: 获得选项的文本 + en-US: Get value of selected item +--- + +## zh-CN + +默认情况下 `onChange` 里只能拿到 value,如果需要拿到选中的节点文本 label,可以使用 `labelInValue` 属性。 + +选中项的 label 会被包装到 value 中传递给 `onChange` 等函数,此时 value 是一个对象。 + +## en-US + +As a default behavior, the onChange callback can only get the value of the selected item. The labelInValue prop can be used to get the label property of the selected item. + +The label of the selected item will be packed as an object for passing to the onChange callback. + +````jsx +import { Select } from 'antd'; +const Option = Select.Option; + +function handleChange(value) { + console.log(value); // { key: "lucy", label: "Lucy (101)" } +} + +ReactDOM.render( +
    + +
    +, mountNode); +```` diff --git a/components/select/demo/multiple.md b/components/select/demo/multiple.md index a4ef77702c..1f73f378ed 100644 --- a/components/select/demo/multiple.md +++ b/components/select/demo/multiple.md @@ -1,11 +1,17 @@ -# 多选 +--- +order: 2 +title: + zh-CN: 多选 + en-US: mutiple selection +--- -- order: 2 +## zh-CN 多选,从已有条目中选择(scroll the menu) ---- +## en-US +Multiple selection, selecting from existing items (scroll the menu). ````jsx import { Select } from 'antd'; @@ -21,9 +27,13 @@ function handleChange(value) { } ReactDOM.render( - {children} , mountNode); diff --git a/components/select/demo/optgroup.md b/components/select/demo/optgroup.md index 35e5ce4b4c..9eefabe2b4 100644 --- a/components/select/demo/optgroup.md +++ b/components/select/demo/optgroup.md @@ -1,10 +1,17 @@ -# 分组 +--- +order: 5 +title: + zh-CN: 分组 + en-US: Option Group +--- -- order: 5 +## zh-CN 用 `OptGroup` 进行选项分组。 ---- +## en-US + +Using `OptGroup` to group the options. ````jsx import { Select } from 'antd'; @@ -19,7 +26,8 @@ ReactDOM.render( - {options} - -
    - -
    - +
    + + +
    + +
    +
    +
    ); }, }); diff --git a/components/select/demo/search.md b/components/select/demo/search.md index 3ab81a4c84..58a62930ca 100644 --- a/components/select/demo/search.md +++ b/components/select/demo/search.md @@ -1,10 +1,15 @@ -# 带搜索框 - -- order: 1 - -在浮层内顶部有搜索框的单项选择器。 - --- +order: 1 +title: + zh-CN: 带搜索框 + en-US: Select with search field +--- + +## zh-CN +展开后可对选项进行搜索。 + +## en-US +Search the options while expanded. ````jsx import { Select } from 'antd'; @@ -20,8 +25,8 @@ ReactDOM.render( placeholder="请选择人员" optionFilterProp="children" notFoundContent="无法找到" - searchPlaceholder="输入关键词" - onChange={handleChange}> + onChange={handleChange} + > diff --git a/components/select/demo/size.md b/components/select/demo/size.md index 369021e879..546ef78326 100644 --- a/components/select/demo/size.md +++ b/components/select/demo/size.md @@ -1,10 +1,17 @@ -# 三种大小 +--- +order: 0 +title: + zh-CN: 三种大小 + en-US: Three sizes +--- -- order: 0 +## zh-CN 三种大小的选择框,当 size 分别为 `large` 和 `small` 时,输入框高度为 `32px` 和 `22px` ,默认高度为 `28px` ---- +## en-US + +The height of the inpub field for the select defaults to 28px. If size is set to large, the height will be 32px, and if set to small, 22px. ````jsx import { Select } from 'antd'; @@ -42,4 +49,8 @@ ReactDOM.render( .code-box-demo .ant-select { margin: 0 8px 10px 0; } + +#components-select-demo-search-box .code-box-demo .ant-select { + margin: 0; +} ```` diff --git a/components/select/demo/tags.md b/components/select/demo/tags.md index c357a9bc40..9be289d92c 100644 --- a/components/select/demo/tags.md +++ b/components/select/demo/tags.md @@ -1,11 +1,17 @@ -# 标签 +--- +order: 3 +title: + zh-CN: 标签 + en-US: Tags +--- -- order: 3 +## zh-CN tags select,随意输入的内容(scroll the menu) ---- +## en-US +Select with tags, transform input to tag (scroll the menu) ````jsx import { Select } from 'antd'; @@ -24,7 +30,8 @@ ReactDOM.render( , mountNode); diff --git a/components/select/index.en-US.md b/components/select/index.en-US.md new file mode 100644 index 0000000000..e314b52413 --- /dev/null +++ b/components/select/index.en-US.md @@ -0,0 +1,59 @@ +--- +category: Components +type: Form Controls +title: Select +--- + +A Selector similar to Select2. + +## When to use + +A dropdown menu for choosing, an elegant alternative to the native select component. + +```html + +``` + +## API + +### Select props + +| Property | Description | Type | Default | +|----------|----------------|----------|--------------| +| value | Current selected option. | string/Array | - | +| defaultValue | Initial selected option. | string/Array | - | +| multiple | Allow multiple select. | boolean | false | +| allowClear | Show clear button, effective in multiple mode only. | boolean | false | +| filterOption | If true, filter options by input, if function, filter options agianst it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded. | boolean or function(inputValue, option) | true | +| tags | When tagging is enabled the user can select from pre-existing options or create a new tag by picking the first choice, which is what the user has typed into the search box so far. | boolean |false | +| onSelect | Called when a option is selected. param is option's value and option instance. | function(value, option) | - | +| onDeselect | Called when a option is deselected. param is option's value. only called for multiple or tags, effective in multiple or tags mode only. | function(value) | - | +| onChange | Called when select an option or input value change, or value of input is changed in combobox mode | function(value, label) | - | +| onSearch | Callback function that is fired when input changed. | function(value: String) | | +| placeholder | Placeholder of select | string | - | +| notFoundContent | Specify content to show when no result matches..| string | 'Not Found' | +| dropdownMatchSelectWidth | Whether dropdown's with is same with select. | boolean | true | +| optionFilterProp | Which prop value of option will be used for filter if filterOption is true | string | value | +| optionLabelProp | Which prop value of option will render as content of select. | string | `children` | +| combobox | Enable combobox mode(can not set multiple at the same time). | boolean | false | +| size | Size of Select input. `large` `small` | String | default | +| showSearch | Whether show search input in single mode.| boolean | false | +| disabled | Whether disabled select | boolean | false | +| getPopupContainer | Parent Node which the selector should be renderd to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative.[example](http://codepen.io/anon/pen/xVBOVQ?editors=001) | Function(triggerNode) | () => document.body | + +### Option props + +| Property | Description | Type | Default | +|----------|----------------|----------|--------------| +| disabled | Disable this option | Boolean | false | +| key | if react request you to set this property, you can set it to value of option, and then ignore value property. | String | | +| value | default to filter with this propery | String | - | + +### OptGroup props + +| Property | Description | Type | Default | +|----------|----------------|----------|-----------------| +| label | Group label | String/React.Element | - | +| key | | String | - | diff --git a/components/select/index.jsx b/components/select/index.jsx deleted file mode 100644 index 9c4bccacfd..0000000000 --- a/components/select/index.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import Select, { Option, OptGroup } from 'rc-select'; -import classNames from 'classnames'; - -const AntSelect = React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-select', - transitionName: 'slide-up', - optionLabelProp: 'children', - choiceTransitionName: 'zoom', - showSearch: false, - }; - }, - render() { - let { - size, className, combobox, notFoundContent - } = this.props; - - const cls = classNames({ - 'ant-select-lg': size === 'large', - 'ant-select-sm': size === 'small', - [className]: !!className, - }); - - if (combobox) { - notFoundContent = null; - } - - return ( - + dropdownMatchSelectWidth={false} + > @@ -34,13 +40,13 @@ const Demo = React.createClass({ - 选项卡一内容 - 选项卡二内容 - 选项卡三内容 + Content of Tab 1 + Content of Tab 2 + Content of Tab 3 ); - } + }, }); ReactDOM.render(, mountNode); diff --git a/components/tabs/demo/size.md b/components/tabs/demo/size.md index 6ddc221c20..bb079f93f4 100644 --- a/components/tabs/demo/size.md +++ b/components/tabs/demo/size.md @@ -1,10 +1,15 @@ -# 迷你型 +--- +order: 5 +title: 迷你型 +--- -- order: 5 +## zh-CN 用在弹出框等较狭窄的容器内。 ---- +## en-US + +Small size can be uesed in Modal. ````jsx import { Tabs } from 'antd'; @@ -12,9 +17,9 @@ const TabPane = Tabs.TabPane; ReactDOM.render( - 选项卡一内容 - 选项卡二内容 - 选项卡三内容 + Content of tab 1 + Content of tab 2 + Content of tab 3 , mountNode); ```` diff --git a/components/tabs/demo/slide.md b/components/tabs/demo/slide.md index fd411a282a..e04f49b6e2 100644 --- a/components/tabs/demo/slide.md +++ b/components/tabs/demo/slide.md @@ -1,10 +1,15 @@ -# 滑动 +--- +order: 3 +title: 滑动 +--- -- order: 3 +## zh-CN 可以左右滑动,容纳更多标签。 ---- +## en-US + +Tab can be slide to left or right, which is used for a lot of tabs. ````jsx import { Tabs } from 'antd'; @@ -12,15 +17,15 @@ const TabPane = Tabs.TabPane; ReactDOM.render( - 选项卡一 - 选项卡二 - 选项卡三 - 选项卡四 - 选项卡五 - 选项卡六 - 选项卡七 - 选项卡八 - 选项卡九 + Content of tab 1 + Content of tab 2 + Content of tab 3 + Content of tab 4 + Content of tab 5 + Content of tab 6 + Content of tab 7 + Content of tab 8 + Content of tab 9 , mountNode); ```` diff --git a/components/tabs/index.en-US.md b/components/tabs/index.en-US.md new file mode 100644 index 0000000000..9a58df0157 --- /dev/null +++ b/components/tabs/index.en-US.md @@ -0,0 +1,40 @@ +--- +category: Components +type: Navigation +english: Tabs +--- + +Tabs make it easy to switch between different views. + +### When to use + +Ant Design has 3 types Tabs for different situation. + +- Card Tabs: for managing too many closeable views. + +- Normall Tabs: for functional aspects of a page. + +- RadioButton: for secondary tabs. + +## API + +### Tabs + +| Property | Description | Type | Default | +|--------------|-----------------------|----------|--------------| +| activeKey | Current TabPane's key| String | _ | +| defaultActiveKey | Default actived tabPanel's key, if activeKey is not setted. | - | +| onChange | Callback when tab is switched | Function | - | +| onTabClick | Callback when tab is clicked | Function | - | +| tabBarExtraContent | Extra element in tab bar | React Node | - | +| type | Basic style of tabs. Options: line, card & editable-card | String | line | +| size | Tab bar size. Options: default, small | String | default | +| tabPosition | Position of tabs. Options: top, right, bottom & left | String | top | +| onEdit | Callback when tab is added or removed, which is executing when set type as editable-card | Function(targetKey, action) | - | +| hideAdd | Hide plus icon or not, which is effective when set type as editable-card | Boolean | false | + +### Tabs.TabPane +| Property | Description | Type | Default | +|--------------|-----------------------|----------|--------------| +| key | TabPane's key | String | _ | +| tab | Show text in TabPane's head | React.Element or String | _ | \ No newline at end of file diff --git a/components/tabs/index.jsx b/components/tabs/index.jsx deleted file mode 100644 index 29613f0076..0000000000 --- a/components/tabs/index.jsx +++ /dev/null @@ -1,87 +0,0 @@ -import Tabs from 'rc-tabs'; -import React, { cloneElement } from 'react'; -import classNames from 'classnames'; -import Icon from '../icon'; - -class AntTabs extends React.Component { - constructor(props) { - super(props); - [ - 'createNewTab', - 'removeTab', - 'handleChange', - ].forEach((method) => this[method] = this[method].bind(this)); - } - createNewTab(targetKey) { - this.props.onEdit(targetKey, 'add'); - } - removeTab(targetKey, e) { - e.stopPropagation(); - if (!targetKey) { - return; - } - this.props.onEdit(targetKey, 'remove'); - } - handleChange(activeKey) { - this.props.onChange(activeKey); - } - render() { - let { prefixCls, size, tabPosition, animation, type, - children, tabBarExtraContent } = this.props; - let className = classNames({ - [this.props.className]: !!this.props.className, - [`${prefixCls}-mini`]: size === 'small' || size === 'mini', - [`${prefixCls}-vertical`]: tabPosition === 'left' || tabPosition === 'right', - [`${prefixCls}-card`]: type.indexOf('card') >= 0, - [`${prefixCls}-${type}`]: true, - }); - if (tabPosition === 'left' || tabPosition === 'right' || type.indexOf('card') >= 0) { - animation = null; - } - // only card type tabs can be added and closed - if (type === 'editable-card') { - children = children.map((child, index) => { - return cloneElement(child, { - tab:
    - {child.props.tab} - -
    , - key: child.key || index, - }); - }); - // Add new tab handler - tabBarExtraContent = ( - - - {tabBarExtraContent} - - ); - } - - return ( - - {tabBarExtraContent} - - } - onChange={this.handleChange} - animation={animation}> - {children} - - ); - } -} - -AntTabs.defaultProps = { - prefixCls: 'ant-tabs', - animation: 'slide-horizontal', - type: 'line', // or 'card' 'editable-card' - onChange() {}, - onEdit() {}, -}; - -AntTabs.TabPane = Tabs.TabPane; - -export default AntTabs; diff --git a/components/tabs/index.tsx b/components/tabs/index.tsx new file mode 100644 index 0000000000..1fcf5121b9 --- /dev/null +++ b/components/tabs/index.tsx @@ -0,0 +1,118 @@ +import RcTabs, { TabPane } from 'rc-tabs'; +import * as React from 'react'; +import { cloneElement } from 'react'; +import classNames from 'classnames'; +import Icon from '../icon'; + +type TabsType = 'line' | 'card' | 'editable-card' +type TabsPosition = 'top' | 'right' | 'bottom' | 'left'; + +export interface TabsProps { + activeKey?: string; + defaultActiveKey?: string; + hideAdd?: boolean; + onChange?: (activeKey: string) => void; + onTabClick?: Function; + tabBarExtraContent?: React.ReactNode; + type?: TabsType; + tabPosition?: TabsPosition; + onEdit?: (targetKey: string, action: any) => void; + size?: 'default' | 'small'; + style?: React.CSSProperties; + prefixCls?: string; + className?: string; + animation?: string; +} + +// Tabs +export interface TabPaneProps { + /** 选项卡头显示文字 */ + tab: React.ReactNode | string; + style?: React.CSSProperties; +} + +export default class Tabs extends React.Component { + static TabPane: TabPaneProps = TabPane; + + static defaultProps = { + prefixCls: 'ant-tabs', + animation: 'slide-horizontal', + type: 'line', // or 'card' 'editable-card' + onChange() { }, + onEdit() { }, + hideAdd: false, + }; + + createNewTab = (targetKey) => { + this.props.onEdit(targetKey, 'add'); + } + + removeTab = (targetKey, e) => { + e.stopPropagation(); + if (!targetKey) { + return; + } + this.props.onEdit(targetKey, 'remove'); + } + + handleChange = (activeKey) => { + this.props.onChange(activeKey); + } + + render() { + let { prefixCls, size, tabPosition, animation, type, + children, tabBarExtraContent, hideAdd } = this.props; + let className = classNames({ + [this.props.className]: !!this.props.className, + [`${prefixCls}-mini`]: size === 'small' || size === 'mini', + [`${prefixCls}-vertical`]: tabPosition === 'left' || tabPosition === 'right', + [`${prefixCls}-card`]: type.indexOf('card') >= 0, + [`${prefixCls}-${type}`]: true, + }); + if (tabPosition === 'left' || tabPosition === 'right' || type.indexOf('card') >= 0) { + animation = null; + } + // only card type tabs can be added and closed + if (type === 'editable-card') { + children = Array.isArray(children) ? children : [children]; + children = (children as Array).map((child, index) => { + if (Array.isArray(child)) { + return child; + } + return cloneElement(child, { + tab:
    + {child.props.tab} + this.removeTab(child.key, e) } /> +
    , + key: child.key || index, + }); + }); + // Add new tab handler + if (!hideAdd) { + tabBarExtraContent = ( + + + {tabBarExtraContent} + + ); + } + } + + tabBarExtraContent = tabBarExtraContent ? ( +
    + {tabBarExtraContent} +
    + ) : null; + + return ( + + {children} + + ); + } +} diff --git a/components/tabs/index.md b/components/tabs/index.zh-CN.md similarity index 87% rename from components/tabs/index.md rename to components/tabs/index.zh-CN.md index 1a0127b9f2..eb4abdf142 100644 --- a/components/tabs/index.md +++ b/components/tabs/index.zh-CN.md @@ -1,9 +1,8 @@ -# Tabs - -- category: Components -- chinese: 标签页 -- type: 导航 - +--- +category: Components +chinese: 标签页 +type: Navigation +english: Tabs --- 选项卡切换组件。 @@ -30,8 +29,10 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。 | onTabClick | tab 被点击的回调 | Function | 无 | | tabBarExtraContent | tab bar 上额外的元素 | React Node | 无 | | type | 页签的基本样式,可选 `line`、`card` `editable-card` 类型 | String | 'line' | +| size | 大小,提供 `default` 和 `small` 两种大小 | String | 'default' | | tabPosition | 页签位置,可选值有 `top` `right` `bottom` `left` | String | 'top' | | onEdit | 新增和删除页签的回调,在 `type="editable-card"` 时有效 | Function(targetKey, action) | 无 | +| hideAdd | 是否隐藏加号图标,在 `type="editable-card"` 时有效 | Boolean | false | ### Tabs.TabPane diff --git a/style/components/tabs.less b/components/tabs/style/index.less similarity index 73% rename from style/components/tabs.less rename to components/tabs/style/index.less index dda0bfaca0..9697f6c656 100644 --- a/style/components/tabs.less +++ b/components/tabs/style/index.less @@ -1,15 +1,19 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; + @tab-prefix-cls: ant-tabs; -@effect-duration: .3s; - .@{tab-prefix-cls} { - outline: none; box-sizing: border-box; position: relative; overflow: hidden; .clearfix; color: @text-color; + &-bar { + outline: none; + } + &-ink-bar { z-index: 1; position: absolute; @@ -18,19 +22,11 @@ box-sizing: border-box; height: 2px; background-color: @primary-color; - transform: scaleX(1); + transition: transform 0.3s @ease-in-out; transform-origin: 0 0; - &-transition-forward { - transition: right 0.3s @ease-in-out, - left 0.3s @ease-in-out 0.3s * 0.3; - } - &-transition-backward { - transition: right 0.3s @ease-in-out 0.3s * 0.3, - left 0.3s @ease-in-out; - } } - &-tabs-bar { + &-bar { border-bottom: 1px solid @border-color-base; margin-bottom: 16px; } @@ -124,14 +120,14 @@ } &-nav-scroll { - width: 99999px; overflow: hidden; + white-space: nowrap; } &-nav { box-sizing: border-box; padding-left: 0; - transition: left 0.5s @ease-in-out; + transition: transform 0.5s @ease-in-out; position: relative; margin: 0; list-style: none; @@ -157,9 +153,9 @@ } .@{tab-prefix-cls}-tab { - float: left; + display: inline-block; height: 100%; - margin-right: 28px; + margin-right: 24px; box-sizing: border-box; position: relative; @@ -196,7 +192,7 @@ } &-mini &-tab { - margin-right: 24px; + margin-right: 0; .@{tab-prefix-cls}-tab-inner { padding: 8px 16px; } @@ -216,10 +212,12 @@ animation-play-state: paused; animation-timing-function: @ease-in-out-quint; opacity: 0; + animation-duration: 0.4s; } &-slide-horizontal-backward-enter&-slide-horizontal-backward-enter-active { animation-name: antMoveLeftIn; + transform: translateZ(0); animation-play-state: running; } @@ -232,10 +230,12 @@ .motion-common(); animation-play-state: paused; animation-timing-function: @ease-in-out-quint; + animation-duration: 0.4s; } &-slide-horizontal-backward-leave&-slide-horizontal-backward-leave-active { animation-name: antMoveRightOut; + transform: translateZ(0); animation-play-state: running; } @@ -244,11 +244,13 @@ animation-play-state: paused; animation-timing-function: @ease-in-out-quint; opacity: 0; + animation-duration: 0.4s; } &-slide-horizontal-forward-enter&-slide-horizontal-forward-enter-active { animation-name: antMoveRightIn; animation-play-state: running; + transform: translateZ(0); } &-slide-horizontal-forward-leave { @@ -258,155 +260,148 @@ left: 0; bottom: 0; .motion-common(); + animation-duration: 0.4s; animation-play-state: paused; animation-timing-function: @ease-in-out-quint; } &-slide-horizontal-forward-leave&-slide-horizontal-forward-leave-active { + transform: translateZ(0); animation-name: antMoveLeftOut; animation-play-state: running; } &-vertical { - .@{tab-prefix-cls}-tab { - float: none; - margin-right: 0; - margin-bottom: 16px; - &:last-child { + > .@{tab-prefix-cls}-bar { + border-bottom: 0; + + .@{tab-prefix-cls}-tab { + float: none; + margin-right: 0; + margin-bottom: 16px; + display: block; + &:last-child { + margin-bottom: 0; + } + .@{tab-prefix-cls}-tab-inner { + padding: 8px 24px; + } + } + + .@{tab-prefix-cls}-nav-scroll { + width: auto; + } + + .@{tab-prefix-cls}-nav-container { margin-bottom: 0; } - .@{tab-prefix-cls}-tab-inner { - padding: 8px 24px; + + .@{tab-prefix-cls}-nav-wrap { + margin-bottom: 0; + } + + .@{tab-prefix-cls}-ink-bar { + width: 2px; + left: auto; + height: auto; + top: 0; } } - .@{tab-prefix-cls}-nav-scroll { - width: auto; - } - - .@{tab-prefix-cls}-tabs-bar { - border-bottom: 0; - } - - .@{tab-prefix-cls}-nav-container { - margin-bottom: 0; - } - - .@{tab-prefix-cls}-nav-wrap { - margin-bottom: 0; - } - - .@{tab-prefix-cls}-ink-bar { - width: 2px; - left: auto; - height: auto; - &-transition-forward { - transition: bottom 0.3s @ease-in-out, - top 0.3s @ease-in-out 0.3s * 0.3; - } - &-transition-backward { - transition: bottom 0.3s @ease-in-out 0.3s * 0.3, - top 0.3s @ease-in-out; - } - } - - .@{tab-prefix-cls}-container { - margin-bottom: 0; - } - - .@{tab-prefix-cls}-content { + > .@{tab-prefix-cls}-content { overflow: hidden; width: auto; } } &-vertical&-left { - .@{tab-prefix-cls}-tabs-bar { + > .@{tab-prefix-cls}-bar { float: left; border-right: 1px solid @border-color-split; margin-right: -1px; margin-bottom: 0; - } - .@{tab-prefix-cls}-tab { - .@{tab-prefix-cls}-tab-inner { - text-align: right; + .@{tab-prefix-cls}-tab { + .@{tab-prefix-cls}-tab-inner { + text-align: right; + } + } + .@{tab-prefix-cls}-nav-container { + margin-right: -1px; + } + .@{tab-prefix-cls}-nav-wrap { + margin-right: -1px; + } + .@{tab-prefix-cls}-ink-bar { + right: 1px; } } - .@{tab-prefix-cls}-nav-container { - margin-right: -1px; - } - .@{tab-prefix-cls}-nav-wrap { - margin-right: -1px; - } - .@{tab-prefix-cls}-ink-bar { - right: 1px; - } - .@{tab-prefix-cls}-content { + > .@{tab-prefix-cls}-content { padding-left: 24px; border-left: 1px solid @border-color-split; } } &-vertical&-right { - .@{tab-prefix-cls}-tabs-bar { + > .@{tab-prefix-cls}-bar { float: right; border-left: 1px solid @border-color-split; margin-left: -1px; margin-bottom: 0; + .@{tab-prefix-cls}-nav-container { + margin-left: -1px; + } + .@{tab-prefix-cls}-nav-wrap { + margin-left: -1px; + } + .@{tab-prefix-cls}-ink-bar { + left: 1px; + } } - .@{tab-prefix-cls}-nav-container { - margin-left: -1px; - } - .@{tab-prefix-cls}-nav-wrap { - margin-left: -1px; - } - .@{tab-prefix-cls}-ink-bar { - left: 1px; - } - .@{tab-prefix-cls}-content { + > .@{tab-prefix-cls}-content { padding-right: 24px; border-right: 1px solid @border-color-split; } } - &-bottom &-tabs-bar { + &-bottom > &-bar { margin-bottom: 0; margin-top: 16px; } - // card style - &&-card &-nav-container { + &&-card > &-bar &-nav-container { height: 36px; } - &&-card &-ink-bar { + &&-card > &-bar &-ink-bar { visibility: hidden; } - &&-card &-tab { + &&-card > &-bar &-tab { margin: 0; - border: 1px solid transparent; + border: 1px solid @border-color-base; border-bottom: 0; border-radius: 6px 6px 0 0; transition: all 0.3s @ease-in-out; background: #f9f9f9; margin-right: 2px; } - &&-card &-tab-inner { + &&-card > &-bar &-tab-inner { padding: 7px 16px 6px; transition: all 0.3s @ease-in-out; } - &&-card &-tab-active { + &&-card > &-bar &-tab-active { background: #fff; + transform: translateZ(0); border-color: @border-color-base; color: @primary-color; } - &&-card &-tab-active &-tab-inner { + &&-card > &-bar &-tab-active &-tab-inner { padding-bottom: 7px; + transform: translateZ(0); } - &&-card &-nav-wrap { + &&-card > &-bar &-nav-wrap { margin-bottom: 0; } - &&-card &-tab-inner .anticon-cross { + &&-card > &-bar &-tab-inner .anticon-cross { margin-right: 0; color: #999; transition: all 0.3s @ease-in-out; @@ -422,14 +417,15 @@ } } - &&-editable-card &-tab:not(&-tab-active):hover &-tab-inner { + &&-editable-card > &-bar &-tab:not(&-tab-active):hover &-tab-inner { padding-left: 8px; padding-right: 8px; } - &&-card &-tab-active .anticon-cross, - &&-card &-tab:hover .anticon-cross { + &&-card > &-bar &-tab-active .anticon-cross, + &&-card > &-bar &-tab:hover .anticon-cross { width: 16px; + transform: translateZ(0); } &-extra-content { diff --git a/components/tabs/style/index.tsx b/components/tabs/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/tabs/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/tag/demo/basic.md b/components/tag/demo/basic.md index 89f8a9fa64..183f69a4f2 100644 --- a/components/tag/demo/basic.md +++ b/components/tag/demo/basic.md @@ -1,11 +1,10 @@ -# 基本 - -- order: 0 +--- +order: 0 +title: 基本 +--- 简单的标签展示,添加 closable 表示可关闭。 ---- - ````jsx import { Tag } from 'antd'; @@ -17,6 +16,5 @@ ReactDOM.render(
    标签一 标签二 标签三 - 标签四(链接)
    , mountNode); ```` diff --git a/components/tag/demo/colorful.md b/components/tag/demo/colorful.md index 54c38ca90e..b4f653e6bd 100644 --- a/components/tag/demo/colorful.md +++ b/components/tag/demo/colorful.md @@ -1,18 +1,17 @@ -# 各种类型 - -- order: 1 +--- +order: 1 +title: 各种类型 +--- 四种颜色的标签。 ---- - ````jsx import { Tag } from 'antd'; ReactDOM.render(
    蓝色 绿色 - 黄色 + 黄色 红色
    , mountNode); ```` diff --git a/components/tag/demo/control.md b/components/tag/demo/control.md index 65490b5454..2a3bc093bf 100644 --- a/components/tag/demo/control.md +++ b/components/tag/demo/control.md @@ -1,23 +1,23 @@ -# 数据生成标签 - -- order: 2 - -用数组生成一组标签。 - -> 使用 `afterClose` 而不是 `onClose`,删除时有动画效果。 - --- +order: 2 +title: 动态添加和删除 +--- + +用数组生成一组标签,可以动态添加和删除。 + +> 使用 `afterClose` 删除时有动画效果。 ````jsx import { Tag, Button } from 'antd'; -let index = 2; +let index = 3; const App = React.createClass({ getInitialState() { return { tags: [ - { key: 1, name: '标签一' }, + { key: 1, name: '不可移除' }, { key: 2, name: '标签二' }, + { key: 3, name: '标签三' }, ], }; }, @@ -33,17 +33,18 @@ const App = React.createClass({ this.setState({ tags }); }, render() { + const { tags } = this.state; return (
    - {this.state.tags.map(tag => - {tag.name} + {tags.map(tag => + this.handleClose(tag.key)}> + {tag.name} + )} -
    - -
    +
    ); - } + }, }); ReactDOM.render(, mountNode); diff --git a/components/tag/index.jsx b/components/tag/index.jsx deleted file mode 100644 index 2f195c849e..0000000000 --- a/components/tag/index.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Animate from 'rc-animate'; -import Icon from '../icon'; -import classNames from 'classnames'; - -class AntTag extends React.Component { - constructor(props) { - super(props); - - this.state = { - closing: false, - closed: false, - }; - } - - close(e) { - const dom = ReactDOM.findDOMNode(this); - dom.style.width = `${dom.offsetWidth}px`; - // It's Magic Code, don't know why - dom.style.width = `${dom.offsetWidth}px`; - this.setState({ - closing: true, - }); - this.props.onClose(e); - } - - animationEnd(key, existed) { - if (!existed) { - this.setState({ - closed: true, - closing: false, - }); - this.props.afterClose(); - } - } - - render() { - const { prefixCls, closable, color, className, children, ...restProps } = this.props; - const close = closable ? : ''; - const classString = classNames({ - [prefixCls]: true, - [`${prefixCls}-${color}`]: !!color, - [`${prefixCls}-close`]: this.state.closing, - [className]: !!className, - }); - return ( - - {this.state.closed ? null : ( -
    - {children} - {close} -
    - )} -
    - ); - } -} - -AntTag.defaultProps = { - prefixCls: 'ant-tag', - closable: false, - onClose() {}, - afterClose() {}, -}; - -export default AntTag; diff --git a/components/tag/index.md b/components/tag/index.md index 750e6b74d3..23f8f60811 100644 --- a/components/tag/index.md +++ b/components/tag/index.md @@ -1,9 +1,8 @@ -# Tag - -- category: Components -- chinese: 标签 -- type: 展示 - +--- +category: Components +chinese: 标签 +type: Views +english: Tag --- 进行标记和分类的小标签。 @@ -15,9 +14,9 @@ ## API -| 参数 | 说明 | 类型 | 可选值 | 默认值 | -|----------------|--------------------------------|------------|---------|--------| -| closable | 标签是否可以关闭 | boolean | | false | -| onClose | 关闭时的回调 | function | | 无 | -| afterClose | 动画关闭后的回调 | function | | 无 | -| color | 标签的色彩 | string | blue green yellow red | 无 | +| 参数 | 说明 | 类型 | 默认值 | +|----------------|-------------------------------|------|--------| +| closable | 标签是否可以关闭 | boolean | false | +| onClose | 关闭时的回调 | function(event) | - | +| afterClose | 关闭动画完成后的回调 | function(event) | - | +| color | 标签的色彩:`blue` `green` `yellow` `red` | string | - | diff --git a/components/tag/index.tsx b/components/tag/index.tsx new file mode 100644 index 0000000000..fcb48a56c2 --- /dev/null +++ b/components/tag/index.tsx @@ -0,0 +1,102 @@ +import * as React from 'react'; +import ReactDOM from 'react-dom'; +import Animate from 'rc-animate'; +import Icon from '../icon'; +import classNames from 'classnames'; +import splitObject from '../_util/splitObject'; +import omit from 'object.omit'; + +export interface TagProps { + /** 标签是否可以关闭 */ + closable?: boolean; + /** 关闭时的回调 */ + onClose?: Function; + /** 动画关闭后的回调 */ + afterClose?: Function; + /** 标签的色彩 */ + color?: string; + style?: React.CSSProperties; +} + +export default class Tag extends React.Component { + static defaultProps = { + prefixCls: 'ant-tag', + closable: false, + onClose() { }, + afterClose() { }, + }; + + constructor(props) { + super(props); + + this.state = { + closing: false, + closed: false, + }; + } + + close = (e) => { + this.props.onClose(e); + if (e.defaultPrevented) { + return; + } + const dom = ReactDOM.findDOMNode(this); + dom.style.width = `${dom.getBoundingClientRect().width}px`; + // It's Magic Code, don't know why + dom.style.width = `${dom.getBoundingClientRect().width}px`; + this.setState({ + closing: true, + }); + } + + animationEnd = (key, existed) => { + if (!existed && !this.state.closed) { + this.setState({ + closed: true, + closing: false, + }); + this.props.afterClose(); + } + } + + render() { + const [{ + prefixCls, closable, color, className, children, + }, otherProps] = splitObject( + this.props, + ['prefixCls', 'closable', 'color', 'className', 'children'] + ); + const closeIcon = closable ? : ''; + const classString = classNames({ + [prefixCls]: true, + [`${prefixCls}-${color}`]: !!color, + [`${prefixCls}-close`]: this.state.closing, + [className]: !!className, + }); + // fix https://fb.me/react-unknown-prop + const divProps = omit(otherProps, [ + 'onClose', + 'afterClose', + ]); + return ( + + {this.state.closed ? null : ( +
    + {children} + {closeIcon} +
    + ) } +
    + ); + } +} diff --git a/style/components/tag.less b/components/tag/style/index.less similarity index 74% rename from style/components/tag.less rename to components/tag/style/index.less index f187d431a4..c912e79c6f 100644 --- a/style/components/tag.less +++ b/components/tag/style/index.less @@ -1,5 +1,7 @@ -@import "../mixins/index"; -@tag-prefix-cls: ~"@{css-prefix}tag"; +@import "../../style/themes/default"; +@import "../../style/mixins/index"; + +@tag-prefix-cls: ant-tag; .@{tag-prefix-cls} { display: inline-block; @@ -7,17 +9,18 @@ height: 22px; padding: 0 8px; border-radius: @border-radius-base; - background: #f3f3f3; + border: 1px solid @border-color-split; + background: @background-color-base; font-size: @font-size-base; - margin-right: 4px; - margin-bottom: 8px; transition: all 0.3s @ease-in-out-circ; vertical-align: middle; - opacity: 0.85; + opacity: 1; overflow: hidden; + margin: 2px 4px 2px 0; + cursor: pointer; &:hover { - opacity: 1; + opacity: 0.85; } &, @@ -28,7 +31,7 @@ &-text { a:first-child:last-child { - display: block; + display: inline-block; margin: 0 -8px; padding: 0 8px; } @@ -52,6 +55,7 @@ &-green, &-yellow, &-red { + border: 0; &, a, a:hover, @@ -85,12 +89,12 @@ &-zoom-enter, &-zoom-appear { - animation: antZoomIn .2s @ease-in-out-circ; + animation: antFadeIn .2s @ease-in-out-circ; animation-fill-mode: both; } &-zoom-leave { - animation: antZoomOut .2s @ease-in-out-circ; + animation: antZoomOut .3s @ease-in-out-circ; animation-fill-mode: both; } } diff --git a/components/tag/style/index.tsx b/components/tag/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/tag/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/time-picker/demo/basic.md b/components/time-picker/demo/basic.md index 45d8826cfc..69f780bc93 100644 --- a/components/time-picker/demo/basic.md +++ b/components/time-picker/demo/basic.md @@ -1,19 +1,23 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- -- order: 0 +## zh-CN 最简单的用法。 ---- +## en-US + +The most basic usage. ````jsx import { TimePicker } from 'antd'; -function onChange(time) { - console.log(time); - if (time) { - console.log(time.toLocaleTimeString('zh-CN', { hour12: false })); // Get time string - } +function onChange(time, timeString) { + console.log(time, timeString); } ReactDOM.render( diff --git a/components/time-picker/demo/disable-options.md b/components/time-picker/demo/disable-options.md index 3388b04b1c..e7efeba8a5 100644 --- a/components/time-picker/demo/disable-options.md +++ b/components/time-picker/demo/disable-options.md @@ -1,16 +1,22 @@ -# 禁止选项 - -- order: 5 +--- +order: 5 +title: + zh-CN: 禁止选项 + en-US: Specify the time that cannot be selected +--- +## zh-CN 限制选择 `20:30` 到 `23:30` 这个时间段。 ---- +## en-US + +You can't select the time from `20:30` to `23:30`. ````jsx import { TimePicker } from 'antd'; function newArray(start, end) { - let result = []; + const result = []; for (let i = start; i < end; i++) { result.push(i); } @@ -18,7 +24,7 @@ function newArray(start, end) { } function disabledHours() { - let hours = newArray(0, 60); + const hours = newArray(0, 60); hours.splice(20, 4); return hours; } diff --git a/components/time-picker/demo/disabled.md b/components/time-picker/demo/disabled.md index 62755f0383..67a75f6a7f 100644 --- a/components/time-picker/demo/disabled.md +++ b/components/time-picker/demo/disabled.md @@ -1,10 +1,18 @@ -# 禁用 +--- +order: 4 +title: + zh-CN: 禁用 + en-US: disabled +--- -- order: 4 +## zh-CN 禁用时间选择。 ---- +## en-US + +A disabled state of the `TimePicker`. + ````jsx import { TimePicker } from 'antd'; diff --git a/components/time-picker/demo/hide-options.md b/components/time-picker/demo/hide-options.md index 4762eda4ad..00ca58ec13 100644 --- a/components/time-picker/demo/hide-options.md +++ b/components/time-picker/demo/hide-options.md @@ -1,16 +1,23 @@ -# 只显示部分选项 - -- order: 6 - -通过 `hideDisabledOptions` 将不可选的选项隐藏。 - --- +order: 6 +title: + zh-CN: 只显示部分选项 + en-US: Show part of options. +--- + +## zh-CN + +通过 hideDisabledOptions 将不可选的选项隐藏。 + +## en-US + +use `hideDisabledOptions` to hide the disabled options. ````jsx import { TimePicker } from 'antd'; function newArray(start, end) { - let result = []; + const result = []; for (let i = start; i < end; i++) { result.push(i); } diff --git a/components/time-picker/demo/size.md b/components/time-picker/demo/size.md index c0cde71eca..42ffaf6bc5 100644 --- a/components/time-picker/demo/size.md +++ b/components/time-picker/demo/size.md @@ -1,10 +1,17 @@ -# 三种大小 +--- +order: 2 +title: + zh-CN: 三种大小 + en-US: Three sizes +--- -- order: 2 +## zh-CN 三种大小的输入框,大的用在表单中,中的为默认。 ---- +## en-US + +The input box comes in three sizes. large is used in the form, while the medium size is the default. ````jsx import { TimePicker } from 'antd'; diff --git a/components/time-picker/demo/value.md b/components/time-picker/demo/value.md index a96f5c0def..b75c0f2968 100644 --- a/components/time-picker/demo/value.md +++ b/components/time-picker/demo/value.md @@ -1,10 +1,17 @@ -# 受控组件 +--- +order: 1 +title: + zh-CN: 受控组件 + en-US: Under control +--- -- order: 1 +## zh-CN value 和 onChange 需要配合使用。 ---- +## en-US + +`Value` and `onChange` should be used together, ````jsx import { TimePicker } from 'antd'; @@ -21,7 +28,7 @@ const Test = React.createClass({ }, render() { return ; - } + }, }); ReactDOM.render(, mountNode); diff --git a/components/time-picker/demo/without-seconds.md b/components/time-picker/demo/without-seconds.md index 4ae8340d72..a6a38756a0 100644 --- a/components/time-picker/demo/without-seconds.md +++ b/components/time-picker/demo/without-seconds.md @@ -1,10 +1,17 @@ -# 不展示秒 +--- +order: 3 +title: + zh-CN: 不展示秒 + en-US: Hide the seconds options +--- -- order: 3 +## zh-CN 不展示秒,也不允许选择。 ---- +## en-US + +The `seconds` options are hidden and cannot be selected. ````jsx import { TimePicker } from 'antd'; diff --git a/components/time-picker/index.en-US.md b/components/time-picker/index.en-US.md new file mode 100644 index 0000000000..93474377d2 --- /dev/null +++ b/components/time-picker/index.en-US.md @@ -0,0 +1,38 @@ +--- +category: Components +type: Form Controls +title: TimePicker +--- + +To select/input a time. + +## When to use +-------- + +By clicking the input box, you can select a time from a popup panel. + +## API +--- + +```html + +``` + +> Warning: `TimePicker` is renamed to `TimePicker` after 0.11. + +| Property | Description | Type | Default | +|---------------------|-----|-----|-------| +| defaultValue | to set default time | string or Date | - | +| value | to set time | string or Date | - | +| placeholder | display when there's no value | string | "Select a time" | +| onChange | a callback function, can be executed when the selected time is changing | function(date, dateString) | - | +| format | to set the time format | string | "HH:mm:ss"、"HH:mm"、"mm:ss" | +| disabled | determine whether the TimePicker is disabled | bool | false | +| disabledHours | to specify the hours that cannot be selected | function() | - | +| disabledMinutes | to specify the minites that cannot be selected | function(selectedHour) | - | +| disabledSeconds | to specify the seconds that cannot be selected | function(selectedHour, selectedMinute) | - | +| hideDisabledOptions | hide the options that can not be selected | boolean | false | +| getPopupContainer | to set the container of the floating layer, while the default is to create a div element in body | function(trigger) | - | +| locale | localization configuration | Object | [default](https://github.com/ant-design/ant-design/issues/1270#issuecomment-201181384) | + + diff --git a/components/time-picker/index.jsx b/components/time-picker/index.jsx deleted file mode 100644 index aecc2e727f..0000000000 --- a/components/time-picker/index.jsx +++ /dev/null @@ -1,111 +0,0 @@ -import React from 'react'; -import DateTimeFormat from 'gregorian-calendar-format'; -import TimePicker from 'rc-time-picker/lib/TimePicker'; -import objectAssign from 'object-assign'; -import defaultLocale from './locale/zh_CN'; -import classNames from 'classnames'; -import GregorianCalendar from 'gregorian-calendar'; - -const AntTimePicker = React.createClass({ - getDefaultProps() { - return { - format: 'HH:mm:ss', - prefixCls: 'ant-time-picker', - onChange() { - }, - locale: {}, - align: { - offset: [0, -2], - }, - disabled: false, - disabledHours: undefined, - disabledMinutes: undefined, - disabledSeconds: undefined, - hideDisabledOptions: false, - placement: 'bottomLeft', - transitionName: 'slide-up', - }; - }, - - getFormatter() { - return new DateTimeFormat(this.props.format); - }, - - /** - * 获得输入框的 className - */ - getSizeClass() { - let sizeClass = ''; - if (this.props.size === 'large') { - sizeClass = ' ant-input-lg'; - } else if (this.props.size === 'small') { - sizeClass = ' ant-input-sm'; - } - return sizeClass; - }, - - /** - * 获得输入框的默认值 - */ - parseTimeFromValue(value) { - if (value) { - if (typeof value === 'string') { - return this.getFormatter().parse(value, { - locale: this.getLocale().calendar, - obeyCount: true, - }); - } else if (value instanceof Date) { - let date = new GregorianCalendar(this.getLocale().calendar); - date.setTime(+value); - return date; - } - } - return value; - }, - - handleChange(value) { - this.props.onChange(value ? new Date(value.getTime()) : null); - }, - - getLocale() { - // 统一合并为完整的 Locale - return objectAssign({}, defaultLocale, this.props.locale); - }, - - render() { - const props = objectAssign({}, this.props); - props.placeholder = ('placeholder' in this.props) - ? props.placeholder : this.getLocale().placeholder; - if (props.defaultValue) { - props.defaultValue = this.parseTimeFromValue(props.defaultValue); - } else { - delete props.defaultValue; - } - if (props.value) { - props.value = this.parseTimeFromValue(props.value); - } - let className = classNames({ - [props.className]: !!props.className, - [`${props.prefixCls}-${props.size}`]: true, - }); - if (props.format.indexOf('ss') < 0) { - props.showSecond = false; - } - if (props.format.indexOf('HH') < 0) { - props.showHour = false; - } - - return ( - - ); - } - -}); - -export default AntTimePicker; diff --git a/components/time-picker/index.tsx b/components/time-picker/index.tsx new file mode 100644 index 0000000000..211a566a4d --- /dev/null +++ b/components/time-picker/index.tsx @@ -0,0 +1,145 @@ +import * as React from 'react'; +import DateTimeFormat from 'gregorian-calendar-format'; +import RcTimePicker from 'rc-time-picker/lib/TimePicker'; +import defaultLocale from './locale/zh_CN'; +import classNames from 'classnames'; +import GregorianCalendar from 'gregorian-calendar'; +import assign from 'object-assign'; + +// TimePicker +export interface TimePickerProps { + /** 默认时间 */ + value?: string | Date; + /** 初始默认时间 */ + defaultValue?: string | Date; + /** 展示的时间格式 : "HH:mm:ss"、"HH:mm"、"mm:ss" */ + format?: string; + /** 时间发生变化的回调 */ + onChange?: (Date: Date) => void; + /** 禁用全部操作 */ + disabled?: boolean; + /** 没有值的时候显示的内容 */ + placeholder?: string; + /** 国际化配置 */ + locale?: {}; + /** 隐藏禁止选择的选项 */ + hideDisabledOptions?: boolean; + /** 禁止选择部分小时选项 */ + disabledHours?: Function; + /** 禁止选择部分分钟选项 */ + disabledMinutes?: Function; + /** 禁止选择部分秒选项 */ + disabledSeconds?: Function; + + style?: React.CSSProperties; +} +export default class TimePicker extends React.Component { + static defaultProps = { + format: 'HH:mm:ss', + prefixCls: 'ant-time-picker', + onChange() { + }, + locale: {}, + align: { + offset: [0, -2], + }, + disabled: false, + disabledHours: undefined, + disabledMinutes: undefined, + disabledSeconds: undefined, + hideDisabledOptions: false, + placement: 'bottomLeft', + transitionName: 'slide-up', + }; + + static contextTypes = { + antLocale: React.PropTypes.object, + }; + + getFormatter() { + return new DateTimeFormat(this.props.format, this.getLocale().format); + } + + /** + * 获得输入框的 className + */ + getSizeClass() { + let sizeClass = ''; + if (this.props.size === 'large') { + sizeClass = ' ant-input-lg'; + } else if (this.props.size === 'small') { + sizeClass = ' ant-input-sm'; + } + return sizeClass; + } + + /** + * 获得输入框的默认值 + */ + parseTimeFromValue(value) { + if (value) { + if (typeof value === 'string') { + return this.getFormatter().parse(value, { + locale: this.getLocale().calendar, + obeyCount: true, + }); + } else if (value instanceof Date) { + let date = new GregorianCalendar(this.getLocale().calendar); + date.setTime(+value); + return date; + } + } + return value; + } + + handleChange = (value) => { + this.props.onChange( + value ? new Date(value.getTime()) : null, + value ? this.getFormatter().format(value) : '', + ); + } + + getLocale() { + let locale = defaultLocale; + if (this.context.antLocale && this.context.antLocale.TimePicker) { + locale = this.context.antLocale.TimePicker; + } + // 统一合并为完整的 Locale + return assign({}, locale, this.props.locale); + } + + render() { + const locale = this.getLocale(); + const props = assign({}, this.props); + props.placeholder = ('placeholder' in this.props) + ? props.placeholder : locale.placeholder; + if (props.defaultValue) { + props.defaultValue = this.parseTimeFromValue(props.defaultValue); + } else { + delete props.defaultValue; + } + if (props.value) { + props.value = this.parseTimeFromValue(props.value); + } + let className = classNames({ + [props.className]: !!props.className, + [`${props.prefixCls}-${props.size}`]: !!props.size, + }); + if (props.format.indexOf('ss') < 0) { + props.showSecond = false; + } + if (props.format.indexOf('HH') < 0) { + props.showHour = false; + } + + return ( + + ); + } +} diff --git a/components/time-picker/index.md b/components/time-picker/index.zh-CN.md similarity index 79% rename from components/time-picker/index.md rename to components/time-picker/index.zh-CN.md index 42014b1eb0..350ee43160 100644 --- a/components/time-picker/index.md +++ b/components/time-picker/index.zh-CN.md @@ -1,19 +1,18 @@ -# TimePicker - -- category: Components -- chinese: 时间选择框 -- type: 表单 - +--- +category: Components +chinese: 时间选择框 +type: Form Controls +english: TimePicker --- 输入或选择时间的控件。 -何时使用 +## 何时使用 -------- 当用户需要输入一个时间,可以点击标准输入框,弹出时间面板进行选择。 -API +## API --- ```html @@ -27,13 +26,14 @@ API | defaultValue | 初始默认时间 | string or Date | 无 | | value | 默认时间 | string or Date | 无 | | placeholder | 没有值的时候显示的内容 | string | "请选择时间" | -| onChange | 时间发生变化的回调 | function(Date value) | 无 | +| onChange | 时间发生变化的回调 | function(date, dateString) | 无 | | format | 展示的时间格式 | string | "HH:mm:ss"、"HH:mm"、"mm:ss" | | disabled | 禁用全部操作 | bool | false | | disabledHours | 禁止选择部分小时选项 | function() | 无 | | disabledMinutes | 禁止选择部分分钟选项 | function(selectedHour) | 无 | | disabledSeconds | 禁止选择部分秒选项 | function(selectedHour, selectedMinute) | 无 | | hideDisabledOptions | 隐藏禁止选择的选项 | boolean | false | -| locale | 国际化配置 | Object | [默认配置](https://github.com/ant-design/ant-design/issues/424) | +| getPopupContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | 无 | +| locale | 国际化配置 | Object | [默认配置](https://github.com/ant-design/ant-design/issues/1270#issuecomment-201181384) | diff --git a/components/time-picker/locale/en_US.js b/components/time-picker/locale/en_US.tsx similarity index 58% rename from components/time-picker/locale/en_US.js rename to components/time-picker/locale/en_US.tsx index 2566680873..f5e274cf0c 100644 --- a/components/time-picker/locale/en_US.js +++ b/components/time-picker/locale/en_US.tsx @@ -1,8 +1,7 @@ import TimepickerLocale from 'rc-time-picker/lib/locale/en_US'; - -const locale = { +import assign from 'object-assign'; +const locale = assign({}, { placeholder: 'Select a time', - ... TimepickerLocale, -}; +}, TimepickerLocale); export default locale; diff --git a/components/time-picker/locale/ru_RU.tsx b/components/time-picker/locale/ru_RU.tsx new file mode 100644 index 0000000000..4e2845e90c --- /dev/null +++ b/components/time-picker/locale/ru_RU.tsx @@ -0,0 +1,10 @@ +/** + * Created by Andrey Gayvoronsky on 13/04/16. + */ +import TimepickerLocale from 'rc-time-picker/lib/locale/ru_RU'; +import assign from 'object-assign'; +const locale = assign({}, { + placeholder: 'Выберите время', +}, TimepickerLocale); + +export default locale; diff --git a/components/time-picker/locale/zh_CN.js b/components/time-picker/locale/zh_CN.tsx similarity index 58% rename from components/time-picker/locale/zh_CN.js rename to components/time-picker/locale/zh_CN.tsx index 4db90bf0c6..b7ae75b716 100644 --- a/components/time-picker/locale/zh_CN.js +++ b/components/time-picker/locale/zh_CN.tsx @@ -1,8 +1,7 @@ import TimepickerLocale from 'rc-time-picker/lib/locale/zh_CN'; - -const locale = { +import assign from 'object-assign'; +const locale = assign({}, { placeholder: '请选择时间', - ... TimepickerLocale, -}; +}, TimepickerLocale); export default locale; diff --git a/components/time-picker/style/index.less b/components/time-picker/style/index.less new file mode 100644 index 0000000000..cc119ef7aa --- /dev/null +++ b/components/time-picker/style/index.less @@ -0,0 +1,222 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; +@import "../../input/style/mixin"; + +@timepicker-prefix-cls: ant-time-picker; + +.@{timepicker-prefix-cls}-panel { + max-width: 168px; + z-index: @zindex-picker; + position: absolute; + + &-inner { + display: inline-block; + position: relative; + outline: none; + border: 1px solid @border-color-base; + list-style: none; + font-size: 12px; + text-align: left; + background-color: #fff; + border-radius: @border-radius-base; + box-shadow: @box-shadow-base; + background-clip: padding-box; + border: 1px solid #ccc; + line-height: 1.5; + overflow: hidden; + } + + &-input { + margin: 0; + padding: 0; + border: 0; + width: 100%; + cursor: auto; + line-height: 1.5; + outline: 0; + + &-wrap { + box-sizing: border-box; + position: relative; + padding: 6px; + border-bottom: 1px solid @border-color-split; + } + + &-invalid { + border-color: red; + } + } + + &-clear-btn { + position: absolute; + right: 5px; + cursor: pointer; + overflow: hidden; + width: 20px; + height: 20px; + text-align: center; + line-height: 20px; + top: 5px; + margin: 0; + } + + &-clear-btn:after { + font-size: 12px; + color: #ccc; + display: inline-block; + line-height: 1; + width: 20px; + transition: color 0.3s ease; + .iconfont-font("\e631"); + } + + &-clear-btn:hover:after { + color: #999; + } + + &-narrow &-input-wrap { + max-width: 111px; + } + + &-select { + float: left; + font-size: 12px; + border: 1px solid @border-color-split; + border-width: 0 1px; + margin-left: -1px; + box-sizing: border-box; + width: 56px; + overflow: hidden; + position: relative; // Fix chrome weird render bug + + &:hover { + overflow-y: auto; + } + + &:first-child { + border-left: 0; + margin-left: 0; + } + + &:last-child { + border-right: 0; + } + + ul { + list-style: none; + box-sizing: border-box; + margin: 0; + padding: 0; + width: 100%; + max-height: 144px; + } + + li { + list-style: none; + box-sizing: content-box; + margin: 0; + padding: 0 0 0 16px; + width: 100%; + height: 24px; + line-height: 24px; + text-align: left; + cursor: pointer; + user-select: none; + transition: background 0.3s ease; + } + + li:last-child:after { + content: ''; + height: 120px; + display: block; + } + + li:hover { + background: tint(@primary-color, 90%); + } + + li&-option-selected { + background: @background-color-base; + font-weight: bold; + } + + li&-option-disabled { + color: @btn-disable-color; + &:hover { + background: transparent; + cursor: @cursor-disabled; + } + } + } + + &.slide-up-enter.slide-up-enter-active&-placement-topLeft, + &.slide-up-enter.slide-up-enter-active&-placement-topRight, + &.slide-up-appear.slide-up-appear-active&-placement-topLeft, + &.slide-up-appear.slide-up-appear-active&-placement-topRight { + animation-name: antSlideDownIn; + } + + &.slide-up-enter.slide-up-enter-active&-placement-bottomLeft, + &.slide-up-enter.slide-up-enter-active&-placement-bottomRight, + &.slide-up-appear.slide-up-appear-active&-placement-bottomLeft, + &.slide-up-appear.slide-up-appear-active&-placement-bottomRight { + animation-name: antSlideUpIn; + } + + &.slide-up-leave.slide-up-leave-active&-placement-topLeft, + &.slide-up-leave.slide-up-leave-active&-placement-topRight { + animation-name: antSlideDownOut; + } + + &.slide-up-leave.slide-up-leave-active&-placement-bottomLeft, + &.slide-up-leave.slide-up-leave-active&-placement-bottomRight { + animation-name: antSlideUpOut; + } +} + +.@{timepicker-prefix-cls} { + position: relative; + display: inline-block; + outline: none; + font-size: @font-size-base; + transition: opacity 0.3s ease; + + &-input { + .input; + width: 100px; + } + + &-large &-input { + .input-lg; + } + + &-small &-input { + .input-sm; + } + + &-open { + opacity: 0; + } + + &-icon { + position: absolute; + user-select: none; + transition: all .3s @ease-in-out; + width: 12px; + height: 12px; + line-height: 12px; + right: 8px; + color: #999; + top: 50%; + margin-top: -6px; + &:after { + content: "\e643"; + font-family: "anticon"; + font-size: 12px; + color: #999; + display: inline-block; + line-height: 1; + vertical-align: bottom; + } + } +} diff --git a/components/time-picker/style/index.tsx b/components/time-picker/style/index.tsx new file mode 100644 index 0000000000..0e54062b35 --- /dev/null +++ b/components/time-picker/style/index.tsx @@ -0,0 +1,5 @@ +import '../../style/index.less'; +import './index.less'; + +// style dependencies +import '../../input/style'; diff --git a/components/timeline/Timeline.tsx b/components/timeline/Timeline.tsx new file mode 100644 index 0000000000..63ea13125f --- /dev/null +++ b/components/timeline/Timeline.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import classNames from 'classnames'; +import TimelineItem from './TimelineItem'; +import splitObject from '../_util/splitObject'; + +export interface TimelineProps { + /** 指定最后一个幽灵节点是否存在或内容 */ + pending?: boolean | React.ReactNode; + style?: React.CSSProperties; +} + +export default class Timeline extends React.Component { + static defaultProps = { + prefixCls: 'ant-timeline', + }; + + render() { + const [{ + prefixCls, children, pending, className, + }, restProps] = splitObject(this.props, + ['prefixCls', 'children', 'pending', 'className']); + const pendingNode = typeof pending === 'boolean' ? null : pending; + const classString = classNames({ + [prefixCls]: true, + [`${prefixCls}-pending`]: !!pending, + [className]: className, + }); + return ( +
      + { + React.Children.map(children, (ele, idx) => + React.cloneElement(ele, { + last: idx === children.length - 1, + }) + ) + } + {(!!pending) + ? {pendingNode} + : null} +
    + ); + } +} diff --git a/components/timeline/TimelineItem.tsx b/components/timeline/TimelineItem.tsx new file mode 100644 index 0000000000..a9a4595c60 --- /dev/null +++ b/components/timeline/TimelineItem.tsx @@ -0,0 +1,55 @@ +import * as React from 'react'; +import classNames from 'classnames'; +import splitObject from '../_util/splitObject'; + +// Timeline +export interface TimeLineItemProps { + /** 指定圆圈颜色 */ + color?: string; + dot?: React.ReactNode; + style?: React.CSSProperties; +} + +export default class TimelineItem extends React.Component { + static defaultProps = { + prefixCls: 'ant-timeline', + color: 'blue', + last: false, + pending: false, + }; + + render() { + const [{ + prefixCls, color, last, children, pending, className, dot, + }, restProps] = splitObject(this.props, + ['prefixCls', 'color', 'last', 'children', 'pending', 'className', 'dot']); + + const itemClassName = classNames({ + [`${prefixCls}-item`]: true, + [`${prefixCls}-item-last`]: last, + [`${prefixCls}-item-pending`]: pending, + [className]: className, + }); + + const dotClassName = classNames({ + [`${prefixCls}-item-head`]: true, + [`${prefixCls}-item-head-custom`]: dot, + [`${prefixCls}-item-head-${color}`]: true, + }); + + return ( +
  • +
    +
    + {dot} +
    +
    + {children} +
    +
  • + ); + } +} diff --git a/components/timeline/demo/basic.md b/components/timeline/demo/basic.md index ef660e8978..225a589a3b 100644 --- a/components/timeline/demo/basic.md +++ b/components/timeline/demo/basic.md @@ -1,20 +1,27 @@ -# 基本用法 +--- +order: 0 +title: + zh-CN: 基本用法 + en-US: Basic +--- -- order: 0 +## zh-CN 基本的时间轴。 ---- +## en-US + +Basic timeline. ````jsx import { Timeline } from 'antd'; ReactDOM.render( - - 创建服务现场 2015-09-01 - 初步排除网络异常 2015-09-01 - 技术测试异常 2015-09-01 - 网络异常正在修复 2015-09-01 - + + 创建服务现场 2015-09-01 + 初步排除网络异常 2015-09-01 + 技术测试异常 2015-09-01 + 网络异常正在修复 2015-09-01 + , mountNode); ```` diff --git a/components/timeline/demo/color.md b/components/timeline/demo/color.md index 9435e42e34..9ea5cac62d 100644 --- a/components/timeline/demo/color.md +++ b/components/timeline/demo/color.md @@ -1,28 +1,35 @@ -# 圆圈颜色 +--- +order: 1 +title: + zh-CN: 圆圈颜色 + en-US: Color +--- -- order: 1 +## zh-CN 圆圈颜色,绿色用于已完成、成功状态,红色表示告警或错误状态,蓝色可表示正在进行或其他默认状态。 ---- +## en-US + +Set the color of circles. `green` means completed or success status, `red` means warning or error, and `blue` means ongoing or other default status. ````jsx import { Timeline } from 'antd'; ReactDOM.render( - - 创建服务现场 2015-09-01 - 创建服务现场 2015-09-01 - -

    初步排除网络异常1

    -

    初步排除网络异常2

    -

    初步排除网络异常3 2015-09-01

    -
    - -

    技术测试异常1

    -

    技术测试异常2

    -

    技术测试异常3 2015-09-01

    -
    -
    + + 创建服务现场 2015-09-01 + 创建服务现场 2015-09-01 + +

    初步排除网络异常1

    +

    初步排除网络异常2

    +

    初步排除网络异常3 2015-09-01

    +
    + +

    技术测试异常1

    +

    技术测试异常2

    +

    技术测试异常3 2015-09-01

    +
    +
    , mountNode); ```` diff --git a/components/timeline/demo/custom.md b/components/timeline/demo/custom.md new file mode 100644 index 0000000000..d3dfbd8948 --- /dev/null +++ b/components/timeline/demo/custom.md @@ -0,0 +1,27 @@ +--- +order: 4 +title: + zh-CN: 自定义时间轴点 + en-US: Custom +--- + +## zh-CN + +可以设置为图标或其他自定义元素。 + +## en-US + +Set a node as an icon or other custom element. + +````jsx +import { Timeline, Icon } from 'antd'; + +ReactDOM.render( + + 创建服务现场 2015-09-01 + 初步排除网络异常 2015-09-01 + } color="red">技术测试异常 2015-09-01 + 网络异常正在修复 2015-09-01 + +, mountNode); +```` diff --git a/components/timeline/demo/pending.md b/components/timeline/demo/pending.md index 91ed9c6cea..f8ad3a18b6 100644 --- a/components/timeline/demo/pending.md +++ b/components/timeline/demo/pending.md @@ -1,19 +1,26 @@ -# 最后一个 +--- +order: 2 +title: + zh-CN: 最后一个 + en-US: Last node +--- -- order: 2 +## zh-CN 在最后位置添加一个幽灵节点,表示时间轴未完成,还在记录过程中。可以指定 `pending={true}` 或者 `pending={一个 React 元素}`。 ---- +## en-US + +When the timeline is incomplete and ongoing, put a ghost node at last. set `pending={true}` or `pending={a React Element}`。 ````jsx import { Timeline } from 'antd'; ReactDOM.render( -查看更多}> - 创建服务现场 2015-09-01 - 初步排除网络异常 2015-09-01 - 技术测试异常 2015-09-01 - + 查看更多}> + 创建服务现场 2015-09-01 + 初步排除网络异常 2015-09-01 + 技术测试异常 2015-09-01 + , mountNode); ```` diff --git a/components/timeline/index.en-US.md b/components/timeline/index.en-US.md new file mode 100644 index 0000000000..220391770c --- /dev/null +++ b/components/timeline/index.en-US.md @@ -0,0 +1,40 @@ +--- +category: Components +type: Views +title: Timeline +--- + +Vertical display timeline. + +## When To Use + +- When a series of information need to be lined from top to bottom by time. +- When need a timeline to make a visual connection. + +## API + +```jsx + + step1 2015-09-01 + step2 2015-09-01 + step3 2015-09-01 + step4 2015-09-01 + +``` + +### Timeline + +Timeline + +| Property | Description | Type | Default | +|----------|----------------------------------------|------------|-------| +| pending | to set the last ghost node's existence or its content | boolean or React.Element | false | + +### Timeline.Item + +Node of timeline + +| Property | Description | Type | Default | +|----------|------------------------------------------|------------|-------| +| color | to set the circle's color to `blue, red, green` or other custom colors | string | blue | +| dot | custom timeline dot | React.Element | - | diff --git a/components/timeline/index.jsx b/components/timeline/index.jsx deleted file mode 100644 index 1c6b8765f6..0000000000 --- a/components/timeline/index.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; - -const TimelineItem = React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-timeline', - color: 'blue', - last: false, - pending: false, - }; - }, - render() { - const { prefixCls, color, last, children, pending } = this.props; - const itemClassName = classNames({ - [`${prefixCls}-item`]: true, - [`${prefixCls}-item-last`]: last, - [`${prefixCls}-item-pending`]: pending, - }); - return ( -
  • -
    -
    -
    {children}
    -
  • - ); - } -}); - -const Timeline = React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-timeline', - }; - }, - render() { - const { prefixCls, children, pending } = this.props; - const pendingNode = typeof pending === 'boolean' ? null : pending; - const className = classNames({ - [prefixCls]: true, - [`${prefixCls}-pending`]: !!pending, - }); - return ( -
      - { - React.Children.map(children, (ele, idx) => - React.cloneElement(ele, { - last: idx === children.length - 1, - }) - ) - } - {(!!pending) - ? {pendingNode} - : null} -
    - ); - } -}); - -Timeline.Item = TimelineItem; - -export default Timeline; diff --git a/components/timeline/index.tsx b/components/timeline/index.tsx new file mode 100644 index 0000000000..7f27820a78 --- /dev/null +++ b/components/timeline/index.tsx @@ -0,0 +1,5 @@ +import Timeline from './Timeline'; +import TimelineItem from './TimelineItem'; + +Timeline.Item = TimelineItem; +export default Timeline; diff --git a/components/timeline/index.md b/components/timeline/index.zh-CN.md similarity index 56% rename from components/timeline/index.md rename to components/timeline/index.zh-CN.md index e8c8ac7b63..10b5ea810e 100644 --- a/components/timeline/index.md +++ b/components/timeline/index.zh-CN.md @@ -1,9 +1,8 @@ -# Timeline - -- category: Components -- chinese: 时间轴 -- type: 展示 - +--- +category: Components +chinese: 时间轴 +type: Views +english: Timeline --- 垂直展示的时间流信息。 @@ -28,14 +27,15 @@ 时间轴。 -| 参数 | 说明 | 类型 | 可选值 |默认值 | -|-----------|------------------------------------------|------------|-------|--------| -| pending | 指定最后一个幽灵节点是否存在或内容 | boolean or React.Element | 无 | false | +| 参数 | 说明 | 类型 | 默认值 | +|----------|----------------------------------------|------------|-------| +| pending | 指定最后一个幽灵节点是否存在或内容 | boolean or React.Element | false | ### Timeline.Item 时间轴的每一个节点。 -| 参数 | 说明 | 类型 | 可选值 |默认值 | -|-----------|------------------------------------------|------------|-------|--------| -| color | 指定圆圈颜色。 | string | blue, red, green | blue | +| 参数 | 说明 | 类型 | 默认值 | +|----------|------------------------------------------|------------|-------| +| color | 指定圆圈颜色 `blue, red, green`,或自定义的色值 | string | blue | +| dot | 自定义时间轴点 | React.Element | - | diff --git a/style/components/timeline.less b/components/timeline/style/index.less similarity index 54% rename from style/components/timeline.less rename to components/timeline/style/index.less index 9410c9a1f8..93d0ae34c5 100644 --- a/style/components/timeline.less +++ b/components/timeline/style/index.less @@ -1,13 +1,19 @@ -@import "../mixins/index"; - -@timeline-prefix-cls: ~"@{css-prefix}timeline"; +@import "../../style/themes/default"; +@import "../../style/mixins/index"; +@timeline-prefix-cls: ant-timeline; @timeline-color: @border-color-split; .@{timeline-prefix-cls} { + list-style: none; + margin: 0; + padding: 0; + &-item { position: relative; - padding-bottom: 12px; + padding: 0 0 12px 0; + list-style: none; + margin: 0; &-tail { position: absolute; @@ -27,20 +33,39 @@ height: 12px; background-color: #fff; border-radius: 100px; + border: 2px solid transparent; &-blue { - border: 2px solid @primary-color; + border-color: @primary-color; + color: @primary-color; } &-red { - border: 2px solid @error-color; + border-color: @error-color; + color: @error-color; } &-green { - border: 2px solid @success-color; + border-color: @success-color; + color: @success-color; } } + &-head-custom { + position: absolute; + text-align: center; + width: 40px; + left: -14px; + line-height: 1; + margin-top: 6px; + border: 0; + height: auto; + border-radius: 0; + padding: 3px 0; + font-size: 12px; + transform: translateY(-50%); + } + &-content { padding: 0 0 10px 24px; - font-size: 12px; + font-size: @font-size-base; position: relative; top: -3px; } diff --git a/components/timeline/style/index.tsx b/components/timeline/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/timeline/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/tooltip/demo/basic.md b/components/tooltip/demo/basic.md index eac4e7756a..d5cbdc1ed7 100644 --- a/components/tooltip/demo/basic.md +++ b/components/tooltip/demo/basic.md @@ -1,18 +1,24 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- -- order: 0 +## zh-CN 最简单的用法。 ---- +## en-US + +The simplest usage. ````jsx import { Tooltip } from 'antd'; ReactDOM.render( - - 鼠标移上来就会出现提示 + + Text will show when mouse enter. , mountNode); ```` - diff --git a/components/tooltip/demo/placement.md b/components/tooltip/demo/placement.md index 8104dfd143..3913789bef 100644 --- a/components/tooltip/demo/placement.md +++ b/components/tooltip/demo/placement.md @@ -1,59 +1,66 @@ -# 位置 +--- +order: 1 +title: + zh-CN: 位置 + en-US: Placement +--- -- order: 1 +## zh-CN 位置有 12 个方向。 ---- +## en-US + +The ToolTip has 12 placements choice. ````jsx import { Tooltip } from 'antd'; -const text = 提示文字; +const text = prompt text; ReactDOM.render( diff --git a/components/tooltip/index.en-US.md b/components/tooltip/index.en-US.md new file mode 100644 index 0000000000..909e4ee78d --- /dev/null +++ b/components/tooltip/index.en-US.md @@ -0,0 +1,23 @@ +--- +category: Components +type: Views +title: Tooltip +--- + +A simple text popup tip. + +## When To Use + +- The tip shows while mouse enter, and hides while mouse leave, the ToolTip doesn't support complex text and operation. + +- It can provide a explain of `button/text/opration`, that can cover the usage of the default system `title`. + +## API + +| Property | Description | Type | Default | +|-----------|------------------------------------------|------------|--------| +| placement | to set the position, which can be one of `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` | string | top | +| title | prompt text | string/React.Element | - | +| getTooltipContainer | to set the container of the tip, while the default is to create a `div` element in `body` | Function(triggerNode) | () => document.body | + +You can visit https://github.com/react-component/tooltip for more API. diff --git a/components/tooltip/index.jsx b/components/tooltip/index.jsx deleted file mode 100644 index bb66a0f015..0000000000 --- a/components/tooltip/index.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import Tooltip from 'rc-tooltip'; - -export default React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-tooltip', - placement: 'top', - mouseEnterDelay: 0.1, - mouseLeaveDelay: 0.1 - }; - }, - getInitialState() { - return { - visible: false - }; - }, - onVisibleChange(visible) { - this.setState({ visible }); - }, - render() { - let transitionName = ({ - top: 'zoom-down', - bottom: 'zoom-up', - left: 'zoom-right', - right: 'zoom-left', - topLeft: 'zoom-down', - bottomLeft: 'zoom-up', - leftTop: 'zoom-right', - rightTop: 'zoom-left', - topRight: 'zoom-down', - bottomRight: 'zoom-up', - leftBottom: 'zoom-right', - rightBottom: 'zoom-left', - })[this.props.placement]; - - // Hide tooltip when there is no title - let visible = this.state.visible; - if (!this.props.title) { - visible = false; - } - - return ( - - {this.props.children} - - ); - } -}); diff --git a/components/tooltip/index.tsx b/components/tooltip/index.tsx new file mode 100644 index 0000000000..164073bf0d --- /dev/null +++ b/components/tooltip/index.tsx @@ -0,0 +1,122 @@ +import * as React from 'react'; +const { cloneElement } = React; +import RcTooltip from 'rc-tooltip'; +import getPlacements from '../popover/placements'; + +const placements = getPlacements({ + verticalArrowShift: 8, +}); + +type PopoverPlacement = + 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | + 'topRight' | 'bottomLeft' | 'bottomRight' | 'leftTop' | + 'leftBottom' | 'rightTop' | 'rightBottom' + +// Tooltip +export interface TooltipProps { + /** + 气泡框位置,可选 `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` + `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` + */ + placement?: PopoverPlacement; + /** 提示文字 */ + title: React.ReactNode; + style?: React.CSSProperties; + builtinPlacements?: Object; + /** Style of overlay */ + overlayStyle?: React.CSSProperties; + prefixCls?: string; + /** Callback when display/hide */ + onVisibleChange?: (visible: boolean) => void; + transitionName?: string; + visible?: boolean; + trigger?: 'hover' | 'focus' | 'click'; + overlay?: React.ReactNode; +} + +export default class Tooltip extends React.Component { + static defaultProps = { + prefixCls: 'ant-tooltip', + placement: 'top', + transitionName: 'zoom-big', + mouseEnterDelay: 0.1, + mouseLeaveDelay: 0.1, + onVisibleChange() {}, + }; + + constructor(props) { + super(props); + this.state = { + visible: false, + }; + } + + onVisibleChange = (visible) => { + this.setState({ visible }); + this.props.onVisibleChange(visible); + } + + getPopupDomNode() { + return this.refs.tooltip.getPopupDomNode(); + } + + // 动态设置动画点 + onPopupAlign = (domNode, align) => { + // 当前返回的位置 + const placement = Object.keys(placements).filter( + key => ( + placements[key].points[0] === align.points[0] && + placements[key].points[1] === align.points[1] + ) + )[0]; + if (!placement) { + return; + } + // 根据当前坐标设置动画点 + const rect = domNode.getBoundingClientRect(); + const transformOrigin = { + top: '50%', + left: '50%', + }; + if (placement.indexOf('top') >= 0 || placement.indexOf('Bottom') >= 0) { + transformOrigin.top = `${rect.height - align.offset[1]}px`; + } else if (placement.indexOf('Top') >= 0 || placement.indexOf('bottom') >= 0) { + transformOrigin.top = `${-align.offset[1]}px`; + } + if (placement.indexOf('left') >= 0 || placement.indexOf('Right') >= 0) { + transformOrigin.left = `${rect.width - align.offset[0]}px`; + } else if (placement.indexOf('right') >= 0 || placement.indexOf('Left') >= 0) { + transformOrigin.left = `${-align.offset[0]}px`; + } + domNode.style.transformOrigin = `${transformOrigin.left} ${transformOrigin.top}`; + } + + render() { + const { prefixCls, title, overlay, children, transitionName } = this.props; + // Hide tooltip when there is no title + let visible = this.state.visible; + if (!title && !overlay) { + visible = false; + } + if ('visible' in this.props) { + visible = this.props.visible; + } + const openClassName = this.props.openClassName || `${prefixCls}-open`; + const childrenCls = (children && children.props && children.props.className) + ? `${children.props.className} ${openClassName}` : openClassName; + return ( + + {visible ? cloneElement(children, { className: childrenCls }) : children} + + ); + } +} diff --git a/components/tooltip/index.md b/components/tooltip/index.zh-CN.md similarity index 72% rename from components/tooltip/index.md rename to components/tooltip/index.zh-CN.md index 94e05f3961..488d316a83 100644 --- a/components/tooltip/index.md +++ b/components/tooltip/index.zh-CN.md @@ -1,9 +1,8 @@ -# Tooltip - -- category: Components -- chinese: 文字提示 -- type: 展示 - +--- +category: Components +chinese: 文字提示 +type: Views +english: Tooltip --- 简单的文字提示气泡框。 @@ -20,3 +19,6 @@ |-----------|------------------------------------------|------------|--------| | placement | 气泡框位置,可选 `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` | string | top | | title | 提示文字 | string/React.Element | 无 | +| getTooltipContainer | 浮层渲染父节点。默认渲染到 body 上 | Function(triggerNode) | () => document.body | + +更多 API 可参考:https://github.com/react-component/tooltip diff --git a/style/components/tooltip.less b/components/tooltip/style/index.less similarity index 95% rename from style/components/tooltip.less rename to components/tooltip/style/index.less index 10e03d6e48..a701f62ab9 100644 --- a/style/components/tooltip.less +++ b/components/tooltip/style/index.less @@ -1,11 +1,7 @@ -@import "../mixins/index"; - -@tooltip-prefix-cls: ~"@{css-prefix}tooltip"; - -// -// Tooltips -// -------------------------------------------------- +@import "../../style/themes/default"; +@import "../../style/mixins/index"; +@tooltip-prefix-cls: ant-tooltip; //** Tooltip max width @tooltip-max-width: 250px; //** Tooltip text color @@ -24,7 +20,7 @@ // Base class .@{tooltip-prefix-cls} { position: absolute; - z-index: 1070; + z-index: @zindex-tooltip; display: block; visibility: visible; // remove left/top by yiminghe diff --git a/components/tooltip/style/index.tsx b/components/tooltip/style/index.tsx new file mode 100644 index 0000000000..3a3ab0de59 --- /dev/null +++ b/components/tooltip/style/index.tsx @@ -0,0 +1,2 @@ +import '../../style/index.less'; +import './index.less'; diff --git a/components/transfer/demo/advanced.md b/components/transfer/demo/advanced.md index 219ba4df26..2e77301b15 100644 --- a/components/transfer/demo/advanced.md +++ b/components/transfer/demo/advanced.md @@ -1,10 +1,19 @@ -# 高级用法 +--- +order: 2 +title: + zh-CN: 高级用法 + en-US: Advanced +--- -- order: 2 +## zh-CN 穿梭框高级用法,可配置操作文案,可定制宽高,可对底部进行自定义渲染。 ---- +## en-US + +Advanced Usage of Transfer. + +You can customize the labels of the transfer buttons, the width and height of the columns, and what should be displayed in the footer. ````jsx import { Transfer, Button } from 'antd'; @@ -20,14 +29,14 @@ const App = React.createClass({ this.getMock(); }, getMock() { - let targetKeys = []; - let mockData = []; + const targetKeys = []; + const mockData = []; for (let i = 0; i < 20; i++) { const data = { key: i, - title: `内容${i + 1}`, - description: `内容${i + 1}的描述`, - chosen: Math.random() * 2 > 1 + title: `content${i + 1}`, + description: `description of content${i + 1}`, + chosen: Math.random() * 2 > 1, }; if (data.chosen) { targetKeys.push(data.key); @@ -41,9 +50,10 @@ const App = React.createClass({ }, renderFooter() { return ( - ); }, @@ -56,13 +66,14 @@ const App = React.createClass({ width: 250, height: 300, }} - operations={['向右操作文案', '向左操作文案']} + operations={['to right', 'to left']} targetKeys={this.state.targetKeys} onChange={this.handleChange} render={item => `${item.title}-${item.description}`} - footer={this.renderFooter} /> + footer={this.renderFooter} + /> ); - } + }, }); ReactDOM.render(, mountNode); diff --git a/components/transfer/demo/basic.md b/components/transfer/demo/basic.md index 3c5c682db5..f18402ba4a 100644 --- a/components/transfer/demo/basic.md +++ b/components/transfer/demo/basic.md @@ -1,13 +1,20 @@ -# 基本用法 - -- order: 0 - -最基本的用法。 - +--- +order: 0 +title: + zh-CN: 基本用法 + en-US: Basic --- +## zh-CN + +最基本的用法,展示了 `dataSource`、`targetKeys`、每行的渲染函数 `render` 以及回调函数 `onChange` 的用法。 + +## en-US + +The most basic usage of `Transfer` involves providing the source data and target keys arrays, plus the rendering and change callback functions. + ````jsx -import { Transfer, Button } from 'antd'; +import { Transfer } from 'antd'; const App = React.createClass({ getInitialState() { @@ -20,14 +27,14 @@ const App = React.createClass({ this.getMock(); }, getMock() { - let targetKeys = []; - let mockData = []; + const targetKeys = []; + const mockData = []; for (let i = 0; i < 20; i++) { const data = { key: i, - title: `内容${i + 1}`, - description: `内容${i + 1}的描述`, - chosen: Math.random() * 2 > 1 + title: `content${i + 1}`, + description: `description of content${i + 1}`, + chosen: Math.random() * 2 > 1, }; if (data.chosen) { targetKeys.push(data.key); @@ -40,23 +47,16 @@ const App = React.createClass({ console.log(targetKeys, direction, moveKeys); this.setState({ targetKeys }); }, - renderFooter() { - return ( - - ); - }, render() { return ( item.title} /> + render={item => item.title} + /> ); - } + }, }); ReactDOM.render(, mountNode); diff --git a/components/transfer/demo/custom-item.md b/components/transfer/demo/custom-item.md new file mode 100644 index 0000000000..adf9dd3739 --- /dev/null +++ b/components/transfer/demo/custom-item.md @@ -0,0 +1,79 @@ +--- +order: 3 +title: + zh-CN: 自定义渲染行数据 + en-US: Custom datasource +--- + +## zh-CN + +自定义渲染每一个 Transfer Item,可用于渲染复杂数据。 + +## en-US + +Custom each Transfer Item, and in this way you can render a complex datasource. + +````jsx +import { Transfer } from 'antd'; + +const App = React.createClass({ + getInitialState() { + return { + mockData: [], + targetKeys: [], + }; + }, + componentDidMount() { + this.getMock(); + }, + getMock() { + const targetKeys = []; + const mockData = []; + for (let i = 0; i < 20; i++) { + const data = { + key: i, + title: `content${i + 1}`, + description: `description of content${i + 1}`, + chosen: Math.random() * 2 > 1, + }; + if (data.chosen) { + targetKeys.push(data.key); + } + mockData.push(data); + } + this.setState({ mockData, targetKeys }); + }, + handleChange(targetKeys, direction, moveKeys) { + console.log(targetKeys, direction, moveKeys); + this.setState({ targetKeys }); + }, + renderItem(item) { + const customLabel = ( + + {item.title} - {item.description} + + ); + + return { + label: customLabel, // for displayed item + value: item.title, // for title and filter matching + }; + }, + render() { + return ( + + ); + }, +}); + +ReactDOM.render(, mountNode); +```` diff --git a/components/transfer/demo/search.md b/components/transfer/demo/search.md index 31707a36e2..4260c4b416 100644 --- a/components/transfer/demo/search.md +++ b/components/transfer/demo/search.md @@ -1,10 +1,17 @@ -# 带搜索框 - -- order: 1 - -带搜索框的穿梭框。 - --- +order: 1 +title: + zh-CN: 带搜索框 + en-US: Search +--- + +## zh-CN + +带搜索框的穿梭框,可以自定义搜索函数。 + +## en-US + +Transfer with a search box. ````jsx import { Transfer } from 'antd'; @@ -20,14 +27,14 @@ const App = React.createClass({ this.getMock(); }, getMock() { - let targetKeys = []; - let mockData = []; + const targetKeys = []; + const mockData = []; for (let i = 0; i < 20; i++) { const data = { key: i, - title: `内容${i + 1}`, - description: `内容${i + 1}的描述`, - chosen: Math.random() * 2 > 1 + title: `content${i + 1}`, + description: `description of content${i + 1}`, + chosen: Math.random() * 2 > 1, }; if (data.chosen) { targetKeys.push(data.key); @@ -36,6 +43,9 @@ const App = React.createClass({ } this.setState({ mockData, targetKeys }); }, + filterOption(inputValue, option) { + return option.description.indexOf(inputValue) > -1; + }, handleChange(targetKeys) { this.setState({ targetKeys }); }, @@ -44,12 +54,13 @@ const App = React.createClass({ item.title} /> + render={item => item.title} + /> ); - } + }, }); ReactDOM.render(, mountNode); diff --git a/components/transfer/index.en-US.md b/components/transfer/index.en-US.md new file mode 100644 index 0000000000..aeb61e6075 --- /dev/null +++ b/components/transfer/index.en-US.md @@ -0,0 +1,43 @@ +--- +category: Components +type: Form Controls +cols: 1 +title: Transfer +--- + +Double column transfer choice box. One or more elements can be selected from either column, one click on the proper 'direction' button, and the transfer is done. + +The left column is considered the 'source' and the right column is considered the 'target'. As you can see in the API description, these names are reflected in. + +## When To Use + +To transfer the elements between two columns in an intuitive and efficient way. + +## API + + +| Property | Description | Type | Default | +|-----------|------------------------------------------|------------|--------| +| dataSource | Used for setting the source data. The elements that are part of this array will be present the left column. Except the elements whose keys are included in `targetKeys` prop. | Array | [] | +| render | The function to generate the item shown on a column. Based on an record (element of the dataSource array), this function should return a React element which is generated from that record. | Function(record) | | +| targetKeys | A set of keys of elements that are listed on the right column. | Array | [] | +| onChange | A callback function that is executed when the transfer between columns is complete. | Function(targetKeys, direction, moveKeys) | | +| listStyle | A custom CSS style used for rendering the transfer columns. | Object | | +| className | A custom CSS class. | String | | +| titles | A set of titles that are sorted from left to right. | Array | ['源列表', '目的列表'] | +| operations | A set of operations that are sorted form top to bottom. | Array | [] | +| showSearch | If included, a search box is shown on each column. | Boolean | false | +| searchPlaceholder | The hint text of the search box. | String | 'Please input the content' | +| notFoundContent | Text to display when a column is empty. | React.node | 'The list is empty' | +| footer | A function used for rendering the footer. | Function(props) | | + + +## Warning + +According the [standard](http://facebook.github.io/react/docs/multiple-components.html#dynamic-children) of React, the key should always be supplied directly to the elements in the array. In Transfer, the keys should be set on the elements included in `dataSource` array. By default, `key` property is used as an unique identifier. + +If there's no `key` in your data, you should use `rowKey` to specify the key that will be used for uniquely identify each element. +```jsx +// eg. your primary key is `uid` +return record.uid} />; +``` diff --git a/components/transfer/index.jsx b/components/transfer/index.jsx deleted file mode 100644 index eef2f2b9e6..0000000000 --- a/components/transfer/index.jsx +++ /dev/null @@ -1,254 +0,0 @@ -import React, { Component, PropTypes } from 'react'; -import List from './list'; -import Operation from './operation'; -import Search from './search'; -import classNames from 'classnames'; - -function noop() { -} - -class Transfer extends Component { - - constructor(props) { - super(props); - - this.state = { - leftFilter: '', - rightFilter: '', - leftCheckedKeys: [], - rightCheckedKeys: [], - }; - } - - splitDataSource() { - const { targetKeys, dataSource } = this.props; - - let leftDataSource = [...dataSource]; - let rightDataSource = []; - - if (targetKeys.length > 0) { - targetKeys.forEach((targetKey) => { - rightDataSource.push(leftDataSource.filter((data, index) => { - if (data.key === targetKey) { - leftDataSource.splice(index, 1); - return true; - } - return false; - })[0]); - }); - } - - return { - leftDataSource, - rightDataSource, - }; - } - - moveTo(direction) { - const { targetKeys } = this.props; - const { leftCheckedKeys, rightCheckedKeys } = this.state; - const moveKeys = direction === 'right' ? leftCheckedKeys : rightCheckedKeys; - // move items to target box - const newTargetKeys = direction === 'right' - ? moveKeys.concat(targetKeys) - : targetKeys.filter(targetKey => !moveKeys.some(checkedKey => targetKey === checkedKey)); - - // empty checked keys - this.setState({ - [direction === 'right' ? 'leftCheckedKeys' : 'rightCheckedKeys']: [], - }); - - this.props.onChange(newTargetKeys, direction, moveKeys); - } - - getGlobalCheckStatus(direction) { - const { leftDataSource, rightDataSource } = this.splitDataSource(); - const { leftFilter, rightFilter, leftCheckedKeys, rightCheckedKeys } = this.state; - - const dataSource = direction === 'left' ? leftDataSource : rightDataSource; - const filter = direction === 'left' ? leftFilter : rightFilter; - const checkedKeys = direction === 'left' ? leftCheckedKeys : rightCheckedKeys; - const filteredDataSource = this.filterDataSource(dataSource, filter); - - let globalCheckStatus; - - if (checkedKeys.length > 0) { - if (checkedKeys.length < filteredDataSource.length) { - globalCheckStatus = 'part'; - } else { - globalCheckStatus = 'all'; - } - } else { - globalCheckStatus = 'none'; - } - return globalCheckStatus; - } - - filterDataSource(dataSource, filter) { - return dataSource.filter(item => { - const itemText = this.props.render(item); - return this.matchFilter(itemText, filter); - }); - } - - matchFilter(text, filterText) { - const regex = new RegExp(filterText); - return text.match(regex); - } - - handleSelectAll(direction) { - const { leftDataSource, rightDataSource } = this.splitDataSource(); - const { leftFilter, rightFilter } = this.state; - const dataSource = direction === 'left' ? leftDataSource : rightDataSource; - const filter = direction === 'left' ? leftFilter : rightFilter; - const checkStatus = this.getGlobalCheckStatus(direction); - const holder = (checkStatus === 'all') ? [] : - this.filterDataSource(dataSource, filter).map(item => item.key); - - this.setState({ - [`${direction}CheckedKeys`]: holder, - }); - } - - handleFilter(direction, e) { - this.setState({ - // deselect all - [`${direction}CheckedKeys`]: [], - // add filter - [`${direction}Filter`]: e.target.value, - }); - } - - handleClear(direction) { - this.setState({ - [`${direction}Filter`]: '', - }); - } - - handleSelect(direction, selectedItem, checked) { - const { leftCheckedKeys, rightCheckedKeys } = this.state; - const holder = direction === 'left' ? leftCheckedKeys : rightCheckedKeys; - let index; - holder.forEach((key, i) => { - if (key === selectedItem.key) { - index = i; - } - }); - if (index > -1) { - holder.splice(index, 1); - } - if (checked) { - holder.push(selectedItem.key); - } - this.setState({ - [`${direction}CheckedKeys`]: holder, - }); - } - - render() { - const { - prefixCls, titles, operations, showSearch, notFoundContent, - searchPlaceholder, body, footer, listStyle, className, - } = this.props; - const { leftFilter, rightFilter, leftCheckedKeys, rightCheckedKeys } = this.state; - - const { leftDataSource, rightDataSource } = this.splitDataSource(); - const leftActive = rightCheckedKeys.length > 0; - const rightActive = leftCheckedKeys.length > 0; - - const leftCheckStatus = this.getGlobalCheckStatus('left'); - const rightCheckStatus = this.getGlobalCheckStatus('right'); - - const cls = classNames({ - [className]: !!className, - prefixCls: true, - }); - - return ( -
    - - - -
    - ); - } -} - -Transfer.defaultProps = { - prefixCls: 'ant-transfer', - dataSource: [], - render: noop, - targetKeys: [], - onChange: noop, - titles: ['源列表', '目的列表'], - operations: [], - showSearch: false, - searchPlaceholder: '请输入搜索内容', - notFoundContent: 'Not Found', - body: noop, - footer: noop, -}; - -Transfer.propTypes = { - prefixCls: PropTypes.string, - dataSource: PropTypes.array, - render: PropTypes.func, - targetKeys: PropTypes.array, - onChange: PropTypes.func, - height: PropTypes.number, - listStyle: PropTypes.object, - className: PropTypes.string, - titles: PropTypes.array, - operations: PropTypes.array, - showSearch: PropTypes.bool, - searchPlaceholder: PropTypes.string, - notFoundContent: PropTypes.node, - body: PropTypes.func, - footer: PropTypes.func, -}; - -Transfer.List = List; -Transfer.Operation = Operation; -Transfer.Search = Search; - -export default Transfer; diff --git a/components/transfer/index.md b/components/transfer/index.md deleted file mode 100644 index 51326c12f9..0000000000 --- a/components/transfer/index.md +++ /dev/null @@ -1,32 +0,0 @@ -# Transfer - -- category: Components -- chinese: 穿梭框 -- type: 表单 -- cols: 1 - ---- - -双栏穿梭选择框。 - -## 何时使用 - -用直观的方式在两栏中移动元素,完成选择行为。 - -## API - - -| 参数 | 说明 | 类型 | 默认值 | -|-----------|------------------------------------------|------------|--------| -| dataSource | 数据源 | Array | [] | -| render | 每行数据渲染函数 | Function(record) | | -| targetKeys | 显示在右侧框数据的key集合 | Array | [] | -| onChange | 变化时回调函数 | Function(targetKeys, direction, moveKeys) | | -| listStyle | 两个穿梭框的自定义样式 | Object | | -| className | 自定义类 | String | | -| titles | 标题集合,顺序从左至右 | Array | ['源列表', '目的列表'] | -| operations | 操作文案集合,顺序从上至下 | Array | [] | -| showSearch | 是否显示搜索框 | Boolean | false | -| searchPlaceholder | 搜索框的默认值 | String | 请输入搜索的内容 | -| notFoundContent | 当列表为空时显示的内容 | React.node | 'Not Found' | -| footer | 底部渲染函数 | Function(props) | | | diff --git a/components/transfer/index.tsx b/components/transfer/index.tsx new file mode 100644 index 0000000000..90bfdfef16 --- /dev/null +++ b/components/transfer/index.tsx @@ -0,0 +1,287 @@ +import * as React from 'react'; +import { PropTypes } from 'react'; +import List from './list'; +import Operation from './operation'; +import Search from './search'; +import classNames from 'classnames'; + +function noop() { +} + +export interface TransferItem { + key: number | string; + title: string; + description?: string; + chosen: boolean; +} + +// Transfer +export interface TransferProps { + dataSource: Array; + render?: (record: TransferItem) => any; + targetKeys: Array; + onChange?: (targetKeys: Array, direction: string, moveKeys: any) => void; + listStyle?: React.CSSProperties; + className?: string; + prefixCls?: string; + titles?: Array; + operations?: Array; + showSearch?: boolean; + searchPlaceholder?: string; + notFoundContent?: React.ReactNode | string; + footer?: (props: any) => any; + style?: React.CSSProperties; + filterOption: (filterText: any, item: any) => boolean; + body?: (props: any) => any; +} + +export default class Transfer extends React.Component { + static List = List; + static Operation = Operation; + static Search = Search; + + static defaultProps = { + prefixCls: 'ant-transfer', + dataSource: [], + render: noop, + targetKeys: [], + onChange: noop, + titles: ['源列表', '目的列表'], + operations: [], + showSearch: false, + body: noop, + footer: noop, + }; + + static propTypes = { + prefixCls: PropTypes.string, + dataSource: PropTypes.array, + render: PropTypes.func, + targetKeys: PropTypes.array, + onChange: PropTypes.func, + height: PropTypes.number, + listStyle: PropTypes.object, + className: PropTypes.string, + titles: PropTypes.array, + operations: PropTypes.array, + showSearch: PropTypes.bool, + filterOption: PropTypes.func, + searchPlaceholder: PropTypes.string, + notFoundContent: PropTypes.node, + body: PropTypes.func, + footer: PropTypes.func, + rowKey: PropTypes.func, + }; + + splitedDataSource: any; + + constructor(props) { + super(props); + this.state = { + leftFilter: '', + rightFilter: '', + leftCheckedKeys: [], + rightCheckedKeys: [], + }; + } + + componentWillReceiveProps(nextProps) { + const { leftCheckedKeys, rightCheckedKeys } = this.state; + if (nextProps.targetKeys !== this.props.targetKeys || + nextProps.dataSource !== this.props.dataSource) { + // clear cached splited dataSource + this.splitedDataSource = null; + + const { dataSource, targetKeys } = nextProps; + // clear key nolonger existed + // clear checkedKeys according to targetKeys + this.setState({ + leftCheckedKeys: leftCheckedKeys + .filter(data => dataSource.filter(item => item.key === data).length) + .filter(data => targetKeys.filter(key => key === data).length === 0), + rightCheckedKeys: rightCheckedKeys + .filter(data => dataSource.filter(item => item.key === data).length) + .filter(data => targetKeys.filter(key => key === data).length > 0), + }); + } + } + splitDataSource(props) { + if (this.splitedDataSource) { + return this.splitedDataSource; + } + const { targetKeys } = props; + let { dataSource } = props; + + if (props.rowKey) { + dataSource = dataSource.map(record => { + record.key = props.rowKey(record); + return record; + }); + } + let leftDataSource = [...dataSource]; + let rightDataSource = []; + + if (targetKeys.length > 0) { + targetKeys.forEach((targetKey) => { + rightDataSource.push(leftDataSource.filter((data, index) => { + if (data.key === targetKey) { + leftDataSource.splice(index, 1); + return true; + } + return false; + })[0]); + }); + } + + this.splitedDataSource = { + leftDataSource, + rightDataSource, + }; + + return this.splitedDataSource; + } + + moveTo = (direction) => { + const { targetKeys } = this.props; + const { leftCheckedKeys, rightCheckedKeys } = this.state; + const moveKeys = direction === 'right' ? leftCheckedKeys : rightCheckedKeys; + // move items to target box + const newTargetKeys = direction === 'right' + ? moveKeys.concat(targetKeys) + : targetKeys.filter(targetKey => !moveKeys.some(checkedKey => targetKey === checkedKey)); + + // empty checked keys + this.setState({ + [direction === 'right' ? 'leftCheckedKeys' : 'rightCheckedKeys']: [], + }); + + this.props.onChange(newTargetKeys, direction, moveKeys); + } + + moveToLeft = () => this.moveTo('left') + moveToRight = () => this.moveTo('right') + + handleSelectAll = (direction, filteredDataSource, checkAll) => { + const holder = checkAll ? [] : filteredDataSource.map(item => item.key); + + this.setState({ + [`${direction}CheckedKeys`]: holder, + }); + } + + handleLeftSelectAll = (filteredDataSource, checkAll) => ( + this.handleSelectAll('left', filteredDataSource, checkAll) + ) + handleRightSelectAll = (filteredDataSource, checkAll) => ( + this.handleSelectAll('right', filteredDataSource, checkAll) + ) + + handleFilter = (direction, e) => { + this.setState({ + // add filter + [`${direction}Filter`]: e.target.value, + }); + } + + handleLeftFilter = (e) => this.handleFilter('left', e) + handleRightFilter = (e) => this.handleFilter('right', e) + + handleClear = (direction) => { + this.setState({ + [`${direction}Filter`]: '', + }); + } + + handleLeftClear = () => this.handleClear('left') + handleRightClear = () => this.handleClear('right') + + handleSelect = (direction, selectedItem, checked) => { + const { leftCheckedKeys, rightCheckedKeys } = this.state; + const holder = direction === 'left' ? [...leftCheckedKeys] : [...rightCheckedKeys]; + let index; + holder.forEach((key, i) => { + if (key === selectedItem.key) { + index = i; + } + }); + if (index > -1) { + holder.splice(index, 1); + } + if (checked) { + holder.push(selectedItem.key); + } + this.setState({ + [`${direction}CheckedKeys`]: holder, + }); + } + + handleLeftSelect = (selectedItem, checked) => this.handleSelect('left', selectedItem, checked); + handleRightSelect = (selectedItem, checked) => this.handleSelect('right', selectedItem, checked); + + render() { + const { + prefixCls, titles, operations, showSearch, notFoundContent, + searchPlaceholder, body, footer, listStyle, className, + filterOption, render, + } = this.props; + const { leftFilter, rightFilter, leftCheckedKeys, rightCheckedKeys } = this.state; + + const { leftDataSource, rightDataSource } = this.splitDataSource(this.props); + const leftActive = rightCheckedKeys.length > 0; + const rightActive = leftCheckedKeys.length > 0; + + const cls = classNames({ + [className]: !!className, + [prefixCls]: true, + }); + + return ( +
    + + + +
    + ); + } +} diff --git a/components/transfer/index.zh-CN.md b/components/transfer/index.zh-CN.md new file mode 100644 index 0000000000..799c8dc34c --- /dev/null +++ b/components/transfer/index.zh-CN.md @@ -0,0 +1,45 @@ +--- +category: Components +chinese: 穿梭框 +type: Form Controls +cols: 1 +english: Transfer +--- + +双栏穿梭选择框。选择一个或以上的选项后,点击对应的方向键,可以把选中的选项移动到另一栏。 + +其中,左边一栏为 `source`,右边一栏为 `target`,API 的设计也反应了这两个概念。 + +## 何时使用 + +用直观的方式在两栏中移动元素,完成选择行为。 + +## API + + +| 参数 | 说明 | 类型 | 默认值 | +|-----------|------------------------------------------|------------|--------| +| dataSource | 数据源,其中的数据将会被渲染到左边一栏中,`targetKeys` 中指定的除外。 | Array | [] | +| render | 每行数据渲染函数,该函数的入参为 `dataSource` 中的项,返回值为 React element | Function(record) | | +| targetKeys | 显示在右侧框数据的key集合 | Array | [] | +| onChange | 选项在两栏之间转移时的回调函数 | Function(targetKeys, direction, moveKeys) | | +| listStyle | 两个穿梭框的自定义样式 | Object | | +| className | 自定义类 | String | | +| titles | 标题集合,顺序从左至右 | Array | ['源列表', '目的列表'] | +| operations | 操作文案集合,顺序从上至下 | Array | [] | +| showSearch | 是否显示搜索框 | Boolean | false | +| filterOption | 接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。| Function(inputValue, option) | | +| searchPlaceholder | 搜索框的默认值 | String | '请输入搜索内容' | +| notFoundContent | 当列表为空时显示的内容 | React.node | '列表为空' | +| footer | 底部渲染函数 | Function(props) | | + + +## 注意 + +按照 React 的[规范](http://facebook.github.io/react/docs/multiple-components.html#dynamic-children),所有的组件数组必须绑定 key。在 Transfer 中,`dataSource`里的数据值需要指定 `key` 值。对于 `dataSource` 默认将每列数据的 `key` 属性作为唯一的标识。 + +如果你的数据没有这个属性,务必使用 `rowKey` 来指定数据列的主键。 +```jsx +// 比如你的数据主键是 uid +return record.uid} />; +``` diff --git a/components/transfer/list.jsx b/components/transfer/list.jsx deleted file mode 100644 index 0b34cc2913..0000000000 --- a/components/transfer/list.jsx +++ /dev/null @@ -1,161 +0,0 @@ -import React, { Component, PropTypes } from 'react'; -import Checkbox from '../checkbox'; -import Search from './search'; -import classNames from 'classnames'; -import Animate from 'rc-animate'; - -function noop() { -} - -class TransferList extends Component { - - constructor(props) { - super(props); - this.state = { - mounted: false, - }; - } - - componentDidMount() { - setTimeout(() => { - this.setState({ - mounted: true, - }); - }, 0); - } - - handleSelectALl() { - this.props.handleSelectAll(); - } - - handleSelect(selectedItem) { - const { checkedKeys } = this.props; - const result = checkedKeys.some((key) => key === selectedItem.key); - this.props.handleSelect(selectedItem, !result); - } - - handleFilter(e) { - this.props.handleFilter(e); - } - - handleClear() { - this.props.handleClear(); - } - - renderCheckbox(props) { - const { prefixCls } = props; - const checkboxCls = classNames({ - [`${prefixCls}-checkbox`]: true, - [`${prefixCls}-checkbox-indeterminate`]: props.checkPart, - [`${prefixCls}-checkbox-checked`]: (!props.checkPart) && props.checked, - [`${prefixCls}-checkbox-disabled`]: !!props.disabled, - }); - let customEle = null; - if (typeof props.checkable !== 'boolean') { - customEle = props.checkable; - } - return ( - - {customEle} - - ); - } - - matchFilter(text, filterText) { - const regex = new RegExp(filterText); - return text.match(regex); - } - - render() { - const { prefixCls, dataSource, titleText, filter, checkedKeys, notFoundContent, - checkStatus, body, footer, showSearch, searchPlaceholder } = this.props; - - // Custom Layout - const footerDom = footer({ ...this.props }); - const bodyDom = body({ ...this.props }); - - const listCls = classNames({ - [prefixCls]: true, - [`${prefixCls}-with-footer`]: !!footerDom, - }); - - const showItems = dataSource.filter((item) => { - const itemText = this.props.render(item); - const filterResult = this.matchFilter(itemText, filter); - return !!filterResult; - }).map((item) => { - const renderedText = this.props.render(item); - return ( -
  • - key === item.key)} /> - {renderedText} -
  • - ); - }); - - return ( -
    -
    - {this.renderCheckbox({ - prefixCls: 'ant-transfer', - checked: checkStatus === 'all', - checkPart: checkStatus === 'part', - checkable: - })}{(checkedKeys.length > 0 ? `${checkedKeys.length}/` : '') + dataSource.length} 条 - {titleText} -
    - { bodyDom || -
    - { showSearch ?
    - -
    : null } - - {showItems.length > 0 ? showItems :
    {notFoundContent}
    } -
    -
    } - { footerDom ?
    - { footerDom } -
    : null } -
    - ); - } -} - -TransferList.defaultProps = { - dataSource: [], - titleText: '', - showSearch: false, - searchPlaceholder: '', - handleFilter: noop, - handleSelect: noop, - handleSelectAll: noop, - render: noop, - // advanced - body: noop, - footer: noop, -}; - -TransferList.propTypes = { - prefixCls: PropTypes.string, - dataSource: PropTypes.array, - showSearch: PropTypes.bool, - searchPlaceholder: PropTypes.string, - titleText: PropTypes.string, - style: PropTypes.object, - handleFilter: PropTypes.func, - handleSelect: PropTypes.func, - handleSelectAll: PropTypes.func, - render: PropTypes.func, - body: PropTypes.func, - footer: PropTypes.func, -}; - -export default TransferList; diff --git a/components/transfer/list.tsx b/components/transfer/list.tsx new file mode 100644 index 0000000000..471d952dd3 --- /dev/null +++ b/components/transfer/list.tsx @@ -0,0 +1,265 @@ +import * as React from 'react'; +import { PropTypes } from 'react'; +import Checkbox from '../checkbox'; +import Search from './search'; +import classNames from 'classnames'; +import Animate from 'rc-animate'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import assign from 'object-assign'; +import { TransferItem } from './index'; + +function noop() { +} + +export function isRenderResultPlainObject(result) { + return result && !React.isValidElement(result) && + Object.prototype.toString.call(result) === '[object Object]'; +} + +export interface TransferListProps { + prefixCls?: string; + dataSource: Array; + filter?: string; + showSearch?: boolean; + searchPlaceholder?: string; + titleText?: string; + style?: React.CSSProperties; + handleFilter?: (e: any) => void; + handleSelect?: (selectedItem: any, checked: boolean) => void; + handleSelectAll?: (dataSource: any[], checkAll: boolean) => void; + handleClear?: () => void; + render?: (item: any) => any; + body?: (props: any) => any; + footer?: (props: any) => void; + checkedKeys?: any[]; + checkStatus?: boolean; + position?: string; + notFoundContent?: React.ReactNode | string; + filterOption: (filterText: any, item: any) => boolean; +} + +export interface TransferContext { + antLocale?: { + Transfer?: any, + }; +} + +export default class TransferList extends React.Component { + static defaultProps = { + dataSource: [], + titleText: '', + showSearch: false, + handleClear: noop, + handleFilter: noop, + handleSelect: noop, + handleSelectAll: noop, + render: noop, + // advanced + body: noop, + footer: noop, + }; + + static propTypes = { + prefixCls: PropTypes.string, + dataSource: PropTypes.array, + showSearch: PropTypes.bool, + filterOption: PropTypes.func, + searchPlaceholder: PropTypes.string, + titleText: PropTypes.string, + style: PropTypes.object, + handleClear: PropTypes.func, + handleFilter: PropTypes.func, + handleSelect: PropTypes.func, + handleSelectAll: PropTypes.func, + render: PropTypes.func, + body: PropTypes.func, + footer: PropTypes.func, + }; + + static contextTypes = { + antLocale: React.PropTypes.object, + }; + + context: TransferContext; + timer: any; + + constructor(props) { + super(props); + this.state = { + mounted: false, + }; + } + + componentDidMount() { + this.timer = setTimeout(() => { + this.setState({ + mounted: true, + }); + }, 0); + } + + componentWillUnmount() { + clearTimeout(this.timer); + } + + shouldComponentUpdate(...args) { + return PureRenderMixin.shouldComponentUpdate.apply(this, args); + } + + getCheckStatus(filteredDataSource) { + const { checkedKeys } = this.props; + if (checkedKeys.length === 0) { + return 'none'; + } else if (filteredDataSource.every(item => checkedKeys.indexOf(item.key) >= 0)) { + return 'all'; + } + return 'part'; + } + + handleSelect = (selectedItem) => { + const { checkedKeys } = this.props; + const result = checkedKeys.some((key) => key === selectedItem.key); + this.props.handleSelect(selectedItem, !result); + } + + handleFilter = (e) => { + this.props.handleFilter(e); + } + + handleClear = () => { + this.props.handleClear(); + } + + renderCheckbox({ prefixCls, filteredDataSource, checked, checkPart, disabled, checkable }) { + const checkAll = (!checkPart) && checked; + + const checkboxCls = classNames({ + [`${prefixCls}-checkbox`]: true, + [`${prefixCls}-checkbox-indeterminate`]: checkPart, + [`${prefixCls}-checkbox-checked`]: checkAll, + [`${prefixCls}-checkbox-disabled`]: disabled, + }); + + return ( + this.props.handleSelectAll(filteredDataSource, checkAll)} + > + {(typeof checkable !== 'boolean') ? checkable : null} + + ); + } + + matchFilter(filterText, item, text) { + const filterOption = this.props.filterOption; + if (filterOption) { + return filterOption(filterText, item); + } + return text.indexOf(filterText) >= 0; + } + + render() { + const { prefixCls, dataSource, titleText, filter, checkedKeys, + body, footer, showSearch, render, style } = this.props; + + let { searchPlaceholder, notFoundContent } = this.props; + + // Custom Layout + const footerDom = footer(assign({}, this.props)); + const bodyDom = body(assign({}, this.props)); + + const listCls = classNames({ + [prefixCls]: true, + [`${prefixCls}-with-footer`]: !!footerDom, + }); + + const filteredDataSource = []; + + const showItems = dataSource.map(item => { + const renderResult = render(item); + let renderedText; + let renderedEl; + + if (isRenderResultPlainObject(renderResult)) { + renderedText = renderResult.value; + renderedEl = renderResult.label; + } else { + renderedText = renderResult; + renderedEl = renderResult; + } + + if (filter && filter.trim() && !this.matchFilter(filter, item, renderedText)) { + return null; + } + + filteredDataSource.push(item); + + return ( +
  • this.handleSelect(item)} key={item.key} title={renderedText}> + key === item.key)} /> + {renderedEl} +
  • + ); + }).filter(item => !!item); + + let unit = '条'; + if (this.context.antLocale && + this.context.antLocale.Transfer) { + unit = dataSource.length > 1 + ? this.context.antLocale.Transfer.itemsUnit + : this.context.antLocale.Transfer.itemUnit; + searchPlaceholder = searchPlaceholder + || this.context.antLocale.Transfer.searchPlaceholder; + notFoundContent = notFoundContent + || this.context.antLocale.Transfer.notFoundContent; + } + + const checkStatus = this.getCheckStatus(filteredDataSource); + + return ( +
    +
    + {this.renderCheckbox({ + prefixCls: 'ant-transfer', + checked: checkStatus === 'all', + checkPart: checkStatus === 'part', + checkable: , + filteredDataSource, + disabled: false, + })} + + + {(checkedKeys.length > 0 ? `${checkedKeys.length}/` : '') + dataSource.length} {unit} + + + {titleText} + + +
    + {bodyDom || +
    + {showSearch ?
    + +
    : null} + + {showItems.length > 0 + ? showItems + :
    {notFoundContent || '列表为空'}
    } +
    +
    } + {footerDom ?
    + {footerDom} +
    : null} +
    + ); + } +} diff --git a/components/transfer/operation.jsx b/components/transfer/operation.tsx similarity index 61% rename from components/transfer/operation.jsx rename to components/transfer/operation.tsx index 5a482c1258..b516f97293 100644 --- a/components/transfer/operation.jsx +++ b/components/transfer/operation.tsx @@ -1,11 +1,28 @@ -import React, { Component, PropTypes } from 'react'; +import * as React from 'react'; import Button from '../button'; import Icon from '../icon'; function noop() { } -class TransferOperation extends Component { +export interface TransferOperationProps { + className?: string; + leftArrowText?: string; + rightArrowText?: string; + moveToLeft?: React.FormEventHandler; + moveToRight?: React.FormEventHandler; + leftActive?: boolean; + rightActive?: boolean; +} + +export default class TransferOperation extends React.Component { + static defaultProps = { + leftArrowText: '', + rightArrowText: '', + moveToLeft: noop, + moveToRight: noop, + }; + render() { const { moveToLeft, @@ -35,20 +52,3 @@ class TransferOperation extends Component { ); } } - -TransferOperation.defaultProps = { - leftArrowText: '', - rightArrowText: '', - moveToLeft: noop, - moveToRight: noop, -}; - -TransferOperation.propTypes = { - className: PropTypes.string, - leftArrowText: PropTypes.string, - rightArrowText: PropTypes.string, - moveToLeft: PropTypes.func, - moveToRight: PropTypes.func, -}; - -export default TransferOperation; diff --git a/components/transfer/search.jsx b/components/transfer/search.jsx deleted file mode 100644 index fb3698aa05..0000000000 --- a/components/transfer/search.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { Component, PropTypes } from 'react'; -import Icon from '../icon'; -function noop() { -} - -class Search extends Component { - handleChange(e) { - this.props.onChange(e); - } - - handleClear(e) { - e.preventDefault(); - this.props.handleClear(e); - } - - render() { - const { placeholder, value, prefixCls } = this.props; - return ( -
    - - { value && value.length > 0 ? - - - - : - } -
    - ); - } -} - -Search.defaultProps = { - placeholder: '', - onChange: noop, - handleClear: noop, -}; - -Search.propTypes = { - prefixCls: PropTypes.string, - placeholder: PropTypes.string, - onChange: PropTypes.func, - handleClear: PropTypes.func, -}; - -export default Search; diff --git a/components/transfer/search.tsx b/components/transfer/search.tsx new file mode 100644 index 0000000000..6401d7b497 --- /dev/null +++ b/components/transfer/search.tsx @@ -0,0 +1,46 @@ +import * as React from 'react'; +import Icon from '../icon'; +function noop() { +} + +export interface SearchProps { + prefixCls?: string; + placeholder?: string; + onChange?: (e: React.FormEvent) => void; + handleClear?: (e: React.MouseEvent) => void; + value?: any; +} + +export default class Search extends React.Component { + static defaultProps = { + placeholder: '', + onChange: noop, + handleClear: noop, + }; + + handleChange = (e) => { + this.props.onChange(e); + } + + handleClear = (e) => { + e.preventDefault(); + this.props.handleClear(e); + } + + render() { + const { placeholder, value, prefixCls } = this.props; + return ( +
    + + {value && value.length > 0 ? + + + + : + } +
    + ); + } +} diff --git a/style/components/transfer.less b/components/transfer/style/index.less similarity index 86% rename from style/components/transfer.less rename to components/transfer/style/index.less index fffeebc640..f6ec03d132 100644 --- a/style/components/transfer.less +++ b/components/transfer/style/index.less @@ -1,8 +1,13 @@ -@transfer-prefix-cls: ~"@{css-prefix}transfer"; -.antCheckboxFn(@checkbox-prefix-cls: ant-transfer-checkbox); -.@{transfer-prefix-cls} { +@import "../../style/themes/default"; +@import "../../style/mixins/index"; +@import "../../checkbox/style/mixin"; +@transfer-prefix-cls: ant-transfer; +.antCheckboxFn(@checkbox-prefix-cls: ant-transfer-checkbox); + +.@{transfer-prefix-cls} { position: relative; + line-height: @line-height-base; &-list { font-size: 12px; @@ -30,6 +35,14 @@ line-height: 32px; text-align: center; font-size: 14px; + .anticon { + transition: all .3s; + font-size: 12px; + color: #ccc; + &:hover { + color: #999; + } + } } } @@ -52,7 +65,6 @@ &-body { font-size: 12px; - line-height: 1.5; position: relative; height: 100%; @@ -123,7 +135,7 @@ } .anticon { - .iconfont-size-under-12px(10px); + .iconfont-size-under-12px(8px); } } } diff --git a/components/transfer/style/index.tsx b/components/transfer/style/index.tsx new file mode 100644 index 0000000000..bd8cd93d51 --- /dev/null +++ b/components/transfer/style/index.tsx @@ -0,0 +1,7 @@ +import '../../style/index.less'; +import './index.less'; + +// style dependencies +import '../../checkbox/style'; +import '../../button/style'; +import '../../input/style'; diff --git a/components/tree-select/demo/basic.md b/components/tree-select/demo/basic.md index 7077395235..af8159b5d6 100644 --- a/components/tree-select/demo/basic.md +++ b/components/tree-select/demo/basic.md @@ -1,10 +1,17 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: Basic +--- -- order: 0 +## zh-CN 最简单的用法。 ---- +## en-US + +The most basic usage. ````jsx import { TreeSelect } from 'antd'; @@ -25,10 +32,11 @@ const Demo = React.createClass({ + onChange={this.onChange} + > diff --git a/components/tree-select/demo/checkable.md b/components/tree-select/demo/checkable.md index 154b0fb90e..81d86140eb 100644 --- a/components/tree-select/demo/checkable.md +++ b/components/tree-select/demo/checkable.md @@ -1,39 +1,47 @@ -# 多选 +--- +order: 2 +title: + zh-CN: 多选 + en-US: Multiple +--- -- order: 2 +## zh-CN 多选和勾选框功能。 ---- +## en-US + +Multiple and checkable. ````jsx import { TreeSelect } from 'antd'; +const SHOW_PARENT = TreeSelect.SHOW_PARENT; const treeData = [{ - label: '节点一', + label: 'Node1', value: '0-0', key: '0-0', children: [{ - label: '子节点一', + label: 'Child Node1', value: '0-0-0', key: '0-0-0', - }, { - label: '子节点二', - value: '0-0-1', - key: '0-0-1', }], }, { - label: '节点二', + label: 'Node2', value: '0-1', key: '0-1', children: [{ - label: '子节点三', + label: 'Child Node3', value: '0-1-0', key: '0-1-0', }, { - label: '子节点四', + label: 'Child Node4', value: '0-1-1', key: '0-1-1', + }, { + label: 'Child Node5', + value: '0-1-2', + key: '0-1-2', }], }]; @@ -54,7 +62,8 @@ const Demo = React.createClass({ onChange: this.onChange, multiple: true, treeCheckable: true, - searchPlaceholder: '请选择', + showCheckedStrategy: SHOW_PARENT, + searchPlaceholder: 'Please select', style: { width: 300, }, diff --git a/components/tree-select/demo/treeData.md b/components/tree-select/demo/treeData.md index 3fdbf74bb8..194504b380 100644 --- a/components/tree-select/demo/treeData.md +++ b/components/tree-select/demo/treeData.md @@ -1,29 +1,37 @@ -# 从数据直接生成 +--- +order: 1 +title: + zh-CN: 从数据直接生成 + en-US: Generate form tree data +--- -- order: 1 +## zh-CN 使用 `treeData` 把 JSON 数据直接生成树结构。 ---- +## en-US + +The tree structure can be populated using `treeData` property. This is a quick and easy way to provide the tree content. + ````jsx import { TreeSelect } from 'antd'; const treeData = [{ - label: '节点一', + label: 'Node1', value: '0-0', key: '0-0', children: [{ - label: '子节点一', + label: 'Child Node1', value: '0-0-1', key: '0-0-1', }, { - label: '子节点二', + label: 'Child Node2', value: '0-0-2', key: '0-0-2', }], }, { - label: '节点二', + label: 'Node2', value: '0-1', key: '0-1', }]; @@ -44,9 +52,10 @@ const Demo = React.createClass({ value={this.state.value} dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} treeData={treeData} - placeholder="请选择" + placeholder="Please select" treeDefaultExpandAll - onChange={this.onChange} /> + onChange={this.onChange} + /> ); }, }); diff --git a/components/tree-select/index.en-US.md b/components/tree-select/index.en-US.md new file mode 100644 index 0000000000..7359db71c4 --- /dev/null +++ b/components/tree-select/index.en-US.md @@ -0,0 +1,59 @@ +--- +category: Components +type: Form Controls +title: TreeSelect +--- + +Tree selection control. + +## When to use + +`TreeSelect` is similar with `Select`, but the values are provided in a tree like structure. +Any data whose entries are defined in a hierarchical manner is fit to use this control. Examples of such case may include a corporate hierarchy, a directory structure, and so on. + + +## API + +### Tree props + +Property | Description | Type | Default +-----|-----|-----|------ +value | To set the current selected treeNode(s). | __Default:__ String/Array. __With `labelInValue` set:__ { value: String, label: React.Node }/Array<{ value, label }>. __With `treeCheckStrictly` set(`halfChecked` is set to `false`):__ { value: String, label: React.Node, halfChecked }/Array<{ value, label, halfChecked }>. | - +labelInValue | Determine whether to put `label` into `value`, the type of `value` as specified in the above | Boolean | false +defaultValue | To set the initial selected treeNode(s). | String/Array | - +multiple | Support multiple or not, will be `true` when enable `treeCheckable`. | Boolean | false +onSelect | A callback function, can be executed when you select a treeNode. | function(value, node, extra) | - +onChange | A callback function, can be executed when selected treeNodes or input value change | function(value, label, extra) | - +allowClear | Whether allow clear | Boolean | false +onSearch | A callback function, can be executed when the search input changes. | function(value: String) | - +placeholder | Placeholder of the select input | String | - +searchPlaceholder | Placeholder of the search input | String | - +dropdownStyle | To set the style of the dropdown menu | Object | - +dropdownMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width | Boolean | - +size | To set the size of the select input, options: `large` `small` | String | default +showSearch | Whether to display a search input in the dropdown menu(valid only in the single mode) | Boolean | false +disabled | Disabled or not | Boolean | false +showCheckedStrategy | __Default:__ just show child nodes. __`TreeSelect.SHOW_ALL`:__ show all checked treeNodes (include parent treeNode). __`TreeSelect.SHOW_PARENT`:__ show checked treeNodes (just show parent treeNode). | enum{TreeSelect.SHOW_ALL, TreeSelect.SHOW_PARENT, TreeSelect.SHOW_CHILD } | TreeSelect.SHOW_CHILD +treeDefaultExpandAll | Whether to expand all treeNodes by default | Boolean | false +treeCheckable | Whether to show checkbox on the treeNodes | Boolean | false +treeCheckStrictly | Whether to check nodes precisely(in the `checkable` mode), means parent and child nodes are not associated | Boolean | false +filterTreeNode | Whether to filter treeNodes by input value. The value of `treeNodeFilterProp` is used for filtering by default. | Boolean/Function(inputValue: string, treeNode: TreeNode) (should return Boolean) | Function +treeNodeFilterProp | Will be used for filtering if `filterTreeNode` returns true | String | 'value' +treeNodeLabelProp | Will render as content of select | String | 'title' +treeData | Data of the treeNodes, manual construction work is no longer needed if this property has been set(ensure the Uniqueness of each value) | array<{ value, label, children, [disabled, selectable] }> | [] +treeDataSimpleMode | Enable simple mode of treeData.(treeData should like this: [{id:1, pId:0, value:'1', label:"test1",...},...], pId is parent node's id) | Boolean/Object{ id: 'id', pId: 'pId', rootPId: null } | false +loadData | Load data asynchronously. | function(node) | - +getPopupContainer | To set the container of the dropdown menu. The default is to create a `div` element in `body`, you can reset it to the scrolling area and make a relative reposition. [example](http://codepen.io/anon/pen/xVBOVQ?editors=001) | Function(triggerNode) | () => document.body + +### TreeNode props + +> We recommend you to use `treeData` rather than `TreeNode`, to avoid the trouble of manual construction. + +Property | Description | Type | Default +-----|-----|-----|------ +disabled | Disabled or not | Boolean | false +key | Required property, should be unique in the tree | String | - +value | Will be treated as `treeNodeFilterProp` by default, should be unique in the tree | String | - +title | Content showed on the treeNodes | String/element | '---' +isLeaf | Leaf node or not | Boolean | false + diff --git a/components/tree-select/index.jsx b/components/tree-select/index.jsx deleted file mode 100644 index f261129d82..0000000000 --- a/components/tree-select/index.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; -import TreeSelect, { TreeNode } from 'rc-tree-select'; -import classNames from 'classnames'; - -const AntTreeSelect = React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-select', - transitionName: 'slide-up', - choiceTransitionName: 'zoom', - showSearch: false, - }; - }, - render() { - const props = this.props; - let { - size, className, combobox, notFoundContent, prefixCls - } = this.props; - - const cls = classNames({ - [`${prefixCls}-lg`]: size === 'large', - [`${prefixCls}-sm`]: size === 'small', - [className]: !!className, - }); - - if (combobox) { - notFoundContent = null; - } - - let checkable = props.treeCheckable; - if (checkable) { - checkable = ; - } - - return ( - - ); - } -}); - -AntTreeSelect.TreeNode = TreeNode; -export default AntTreeSelect; diff --git a/components/tree-select/index.md b/components/tree-select/index.md deleted file mode 100644 index c5bfe4ef0a..0000000000 --- a/components/tree-select/index.md +++ /dev/null @@ -1,54 +0,0 @@ -# TreeSelect - -- category: Components -- chinese: 树选择 -- type: 表单 - ---- - -树型选择控件。 - -## 何时使用 - -类似 Select 的选择控件,可选择的数据结构是一个树形结构时,可以使用 TreeSelect,例如公司层级、学科系统、分类目录等等。 - -## API - -### Tree props - -| 参数 | 说明 | 类型 | 默认值 | -|-----------|------------------------------------------|------------|--------| -| value | 指定当前选中的条目 | string/Array | 无 | -| defaultValue | 指定默认选中的条目 | string/Array | 无 | -| multiple | 支持多选 | boolean | false | -| tags | 可以把随意输入的条目作为 tag,输入项不需要与下拉选项匹配 | boolean |false | -| onSelect | 被选中时调用,参数为选中项的 value 值 | function(value) | 无 | -| onChange | 选中option,或input的value变化(combobox 模式下)时,调用此函数 | function(value, label) | 无 | -| allowClear | 显示清除按钮 | boolean | false | -| onSearch | 文本框值变化时回调 | function(value: String) | | -| placeholder | 选择框默认文字 | string | 无 | -| searchPlaceholder | 搜索框默认文字 | string | 无 | -| dropdownStyle | 下拉菜单的样式 | object | 无 | -| dropdownMatchSelectWidth | 下拉菜单和选择器同宽 | boolean | true | -| combobox | 输入框自动提示模式 | boolean | false | -| size | 选择框大小,可选 `large` `small` | String | default | -| showSearch | 在下拉中显示搜索框 | boolean | false | -| disabled | 是否禁用 | boolean | false | -| treeDefaultExpandAll | 默认展开所有树节点 | bool | false | -| treeCheckable | 显示checkbox | bool | false | -| filterTreeNode | 是否根据输入项进行筛选,返回值true | function(treeNode) | - | -| treeNodeFilterProp | 输入项过滤对应的 treeNode 属性 | String | 'value' | -| treeNodeLabelProp | 作为显示的prop设置 | String | 'title' | -| treeData | treeNodes数据,如果设置则不需要手动构造TreeNode节点(如果value在整个树范围内不唯一,需要设置`key`其值为整个树范围内的唯一id | array<{value, label, children}> | [] | -| loadData | 异步加载数据 | function(node) | - | - -### TreeNode props -> 建议使用 treeData 来代替 TreeNode,免去手工构造麻烦 - -| 参数 | 说明 | 类型 | 默认值 | -|-----------|------------------------------------------|------------|--------| -| disabled | 是否禁用 | Boolean | false | -| key | 此项必须设置(其值在整个树范围内唯一) | String | - | -| value | 默认根据此属性值进行筛选 | String | - | -| title | 树节点显示的内容 | String | '---' | -| isLeaf | 是否是叶子节点 | bool | false | diff --git a/components/tree-select/index.tsx b/components/tree-select/index.tsx new file mode 100644 index 0000000000..2aab690bbc --- /dev/null +++ b/components/tree-select/index.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; +import RcTreeSelect, { TreeNode, SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from 'rc-tree-select'; +import classNames from 'classnames'; +import { TreeSelectProps, TreeSelectContext } from './interface'; + +export { TreeSelectProps }; + +export default class TreeSelect extends React.Component { + static TreeNode = TreeNode; + static SHOW_ALL = SHOW_ALL; + static SHOW_PARENT = SHOW_PARENT; + static SHOW_CHILD = SHOW_CHILD; + + static defaultProps = { + prefixCls: 'ant-select', + transitionName: 'slide-up', + choiceTransitionName: 'zoom', + showSearch: false, + dropdownClassName: 'ant-select-tree-dropdown', + }; + + static contextTypes = { + antLocale: React.PropTypes.object, + }; + + context: TreeSelectContext; + + render() { + const props = this.props; + let { + size, className, combobox, notFoundContent, prefixCls, + } = this.props; + + const cls = classNames({ + [`${prefixCls}-lg`]: size === 'large', + [`${prefixCls}-sm`]: size === 'small', + [className]: !!className, + }); + + const { antLocale } = this.context; + if (antLocale && antLocale.Select) { + notFoundContent = notFoundContent || antLocale.Select.notFoundContent; + } + + if (combobox) { + notFoundContent = null; + } + + let checkable = props.treeCheckable; + if (checkable) { + checkable = ; + } + + return ( + + ); + } +} diff --git a/components/tree-select/index.zh-CN.md b/components/tree-select/index.zh-CN.md new file mode 100644 index 0000000000..e595663819 --- /dev/null +++ b/components/tree-select/index.zh-CN.md @@ -0,0 +1,57 @@ +--- +category: Components +chinese: 树选择 +type: Form Controls +english: TreeSelect +--- + +树型选择控件。 + +## 何时使用 + +类似 Select 的选择控件,可选择的数据结构是一个树形结构时,可以使用 TreeSelect,例如公司层级、学科系统、分类目录等等。 + +## API + +### Tree props + +| 参数 | 说明 | 类型 | 默认值 | +|-----------|------------------------------------------|------------|--------| +| value | 指定当前选中的条目 | 通常: String/Array. 设置 labelInValue: {value: String, label: React.Node}/Array<{value, label}>. 设置 treeCheckStrictly(halfChecked 默认为 false): {value: String, label: React.Node, halfChecked}/Array<{value, label, halfChecked}>. | - | +| labelInValue | 是否把 label 嵌入到 value 里,设置后参考以上 value 类型写法 | Boolean | false | +| defaultValue | 指定默认选中的条目 | String/Array | - | +| multiple | 支持多选(当设置 treeCheckable 时自动变为true) | Boolean | false | +| onSelect | 被选中时调用 | function(value, node, extra) | - | +| onChange | 选中树节点时调用此函数 | function(value, label, extra) | - | +| allowClear | 显示清除按钮 | Boolean | false | +| onSearch | 文本框值变化时回调 | function(value: String) | - | +| placeholder | 选择框默认文字 | String | - | +| searchPlaceholder | 搜索框默认文字 | String | - | +| dropdownStyle | 下拉菜单的样式 | Object | - | +| dropdownMatchSelectWidth | 下拉菜单和选择器同宽 | Boolean | true | +| size | 选择框大小,可选 `large` `small` | String | default | +| showSearch | 在下拉中显示搜索框(仅在单选模式下生效) | Boolean | false | +| disabled | 是否禁用 | Boolean | false | +| showCheckedStrategy | `TreeSelect.SHOW_ALL`: 显示所有选中节点(包括父节点). `TreeSelect.SHOW_PARENT`: 只显示父节点(当父节点下所有子节点都选中时). 默认只显示子节点. | enum{TreeSelect.SHOW_ALL, TreeSelect.SHOW_PARENT, TreeSelect.SHOW_CHILD } | TreeSelect.SHOW_CHILD | +| treeDefaultExpandAll | 默认展开所有树节点 | Boolean | false | +| treeCheckable | 显示 checkbox | Boolean | false | +| treeCheckStrictly | checkable 状态下节点选择完全受控(父子节点选中状态不再关联)| Boolean | false | +| filterTreeNode | 是否根据输入项进行筛选,默认用 treeNodeFilterProp 的值作为要筛选的 TreeNode 的属性值 | Boolean/Function(inputValue: string, treeNode: TreeNode) (函数需要返回bool值) | Function | +| treeNodeFilterProp | 输入项过滤对应的 treeNode 属性 | String | 'value' | +| treeNodeLabelProp | 作为显示的 prop 设置 | String | 'title' | +| treeData | treeNodes 数据,如果设置则不需要手动构造 TreeNode 节点(value 在整个树范围内唯一)| array<{value, label, children, [disabled, selectable]}> | [] | +|treeDataSimpleMode | 使用简单格式的 treeData,具体设置参考可设置的类型 (此时 treeData 应变为这样的数据结构: [{id:1, pId:0, value:'1', label:"test1",...},...], `pId` 是父节点的 id) | Boolean/Object{id: 'id', pId: 'pId', rootPId: null} | false | +| loadData | 异步加载数据 | function(node) | - | +| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](http://codepen.io/anon/pen/xVBOVQ?editors=001) | Function(triggerNode) | () => document.body | + +### TreeNode props + +> 建议使用 treeData 来代替 TreeNode,免去手工构造麻烦 + +| 参数 | 说明 | 类型 | 默认值 | +|-----------|------------------------------------------|------------|--------| +| disabled | 是否禁用 | Boolean | false | +| key | 此项必须设置(其值在整个树范围内唯一) | String | - | +| value | 默认根据此属性值进行筛选(其值在整个树范围内唯一) | String | - | +| title | 树节点显示的内容 | String/element | '---' | +| isLeaf | 是否是叶子节点 | Boolean | false | diff --git a/components/tree-select/interface.tsx b/components/tree-select/interface.tsx new file mode 100644 index 0000000000..2004183a92 --- /dev/null +++ b/components/tree-select/interface.tsx @@ -0,0 +1,46 @@ +import * as React from 'react'; + +interface TreeData { + key: string; + value: string; + label: React.ReactNode; + children?: Array; +} + +export interface TreeSelectProps { + style?: React.CSSProperties; + value?: string | Array; + defaultValue?: string | Array; + multiple?: boolean; + tags?: boolean; + onSelect?: (value: any) => void; + onChange?: (value: any, label: any) => void; + allowClear?: boolean; + onSearch?: (value: any) => void; + placeholder?: string; + searchPlaceholder?: string; + dropdownStyle?: React.CSSProperties; + dropdownMatchSelectWidth?: boolean; + combobox?: boolean; + size?: 'large' | 'small'; + showSearch?: boolean; + disabled?: boolean; + treeDefaultExpandAll?: boolean; + treeCheckable?: boolean | React.ReactNode; + filterTreeNode?: (treeNode: any) => boolean; + treeNodeFilterProp?: string; + treeNodeLabelProp?: string; + treeData?: Array; + treeDataSimpleMode?: boolean; + loadData?: (node: any) => void; + showCheckedStrategy?: 'SHOW_ALL' | 'SHOW_PARENT' | 'SHOW_CHILD'; + className?: string; + prefixCls?: string; + notFoundContent?: React.ReactNode; +} + +export interface TreeSelectContext { + antLocale?: { + Select?: any, + }; +} diff --git a/style/components/treeSelect.less b/components/tree-select/style/index.less similarity index 78% rename from style/components/treeSelect.less rename to components/tree-select/style/index.less index 5574cb14d9..9517e52a0e 100644 --- a/style/components/treeSelect.less +++ b/components/tree-select/style/index.less @@ -1,5 +1,9 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; +@import "../../tree/style/mixin"; +@import "../../checkbox/style/mixin"; + @select-tree-prefix-cls: ant-select-tree; -@import "../mixins/iconfont"; .antCheckboxFn(@checkbox-prefix-cls: ant-select-tree-checkbox); @@ -15,8 +19,7 @@ outline: 0; &.filter-node { > a { - color: @error-color!important; - font-weight: bold!important; + font-weight: bold !important; } } ul { @@ -58,15 +61,17 @@ } &.@{select-tree-prefix-cls}-icon_loading { &:after { - content: '\e6a1'; display: inline-block; - font-family: 'anticon'; + .iconfont-font("\e6a1"); font-weight: bold; - .animation(loadingCircle 1s infinite linear); + animation: loadingCircle 1s infinite linear; margin-top: 8px; } } &.@{select-tree-prefix-cls}-switcher { + &.@{select-tree-prefix-cls}-switcher-noop { + cursor: auto; + } &.@{select-tree-prefix-cls}-roots_open, &.@{select-tree-prefix-cls}-center_open, &.@{select-tree-prefix-cls}-bottom_open, @@ -93,9 +98,9 @@ } } &-treenode-disabled { - >span, - >a, - >a span { + > span, + > a, + > a span { color: #ccc; cursor: not-allowed; } @@ -109,3 +114,17 @@ vertical-align: top; } } + +.@{select-tree-prefix-cls}-dropdown .ant-select-dropdown-search + span { + padding: 7px 15px; + color: #ccc; + cursor: not-allowed; + display: block; +} + +.ant-select-not-found { + cursor: not-allowed; + color: #ccc; + padding: 7px 15px; + display: block; +} diff --git a/components/tree-select/style/index.tsx b/components/tree-select/style/index.tsx new file mode 100644 index 0000000000..c3f91d2d60 --- /dev/null +++ b/components/tree-select/style/index.tsx @@ -0,0 +1,6 @@ +import '../../style/index.less'; +import './index.less'; + +// style dependencies +import '../../select/style'; +import '../../checkbox/style'; diff --git a/components/tree/demo/basic-controlled.md b/components/tree/demo/basic-controlled.md index 9a661d4c44..cbf0fa9439 100644 --- a/components/tree/demo/basic-controlled.md +++ b/components/tree/demo/basic-controlled.md @@ -1,10 +1,17 @@ -# 受控操作示例 +--- +order: 1 +title: + zh-CN: 受控操作示例 + en-US: basic controlled example +--- -- order: 1 +## zh-CN 受控操作示例 ---- +## en-US + +basic controlled example ````jsx import { Tree } from 'antd'; @@ -30,73 +37,31 @@ const generateData = (_level, _preKey, _tns) => { if (_level < 0) { return tns; } - const __level = _level - 1; + const level = _level - 1; children.forEach((key, index) => { tns[index].children = []; - return generateData(__level, key, tns[index].children); + return generateData(level, key, tns[index].children); }); }; generateData(z); -function loopData(data, callback) { - const loop = (d, level = 0) => { - d.forEach((item, index) => { - const pos = `${level}-${index}`; - if (item.children) { - loop(item.children, pos); - } - callback(item, index, pos); - }); - }; - loop(data); -} - -function getFilterExpandedKeys(data, expandedKeys) { - const expandedPosArr = []; - loopData(data, (item, index, pos) => { - if (expandedKeys.indexOf(item.key) > -1) { - expandedPosArr.push(pos); - } - }); - const filterExpandedKeys = []; - loopData(data, (item, index, pos) => { - expandedPosArr.forEach(p => { - if ((pos.split('-').length < p.split('-').length - && p.indexOf(pos) === 0 || pos === p) - && filterExpandedKeys.indexOf(item.key) === -1) { - filterExpandedKeys.push(item.key); - } - }); - }); - return filterExpandedKeys; -} - const Demo = React.createClass({ - getDefaultProps() { - return { - multiple: true, - }; - }, getInitialState() { return { - expandedKeys: getFilterExpandedKeys(gData, ['0-0-0', '0-0-1']), + expandedKeys: ['0-0-0', '0-0-1'], + autoExpandParent: true, checkedKeys: ['0-0-0'], selectedKeys: [], }; }, - onExpand(treeNode, expand, expandedKeys) { - console.log('onExpand', expand, expandedKeys); - const index = expandedKeys.indexOf(treeNode.props.eventKey); - if (expand) { - if (index > -1) { - expandedKeys.splice(index, 1); - } - } else { - if (index === -1) { - expandedKeys.push(treeNode.props.eventKey); - } - } - this.setState({ expandedKeys }); + onExpand(expandedKeys) { + console.log('onExpand', arguments); + // if not set autoExpandParent to false, if children expanded, parent can not collapse. + // or, you can remove all expanded chilren keys. + this.setState({ + expandedKeys, + autoExpandParent: false, + }); }, onCheck(checkedKeys) { this.setState({ @@ -120,10 +85,13 @@ const Demo = React.createClass({ return ; }); return ( - + onSelect={this.onSelect} selectedKeys={this.state.selectedKeys} + > {loop(gData)} ); diff --git a/components/tree/demo/basic.md b/components/tree/demo/basic.md index 9fc9101a48..aef36c976b 100644 --- a/components/tree/demo/basic.md +++ b/components/tree/demo/basic.md @@ -1,10 +1,17 @@ -# 基本 +--- +order: 0 +title: + zh-CN: 基本 + en-US: basic +--- -- order: 0 +## zh-CN 最简单的用法,展示可勾选,可选中,禁用,默认展开等功能。 ---- +## en-US + +The most basic usage, tell you how to use checkable, selectable, disabled, defaultExpandKeys, and etc. ````jsx import { Tree } from 'antd'; @@ -24,9 +31,6 @@ const Demo = React.createClass({ defaultCheckedKeys: keys, }; }, - onExpand(treeNode, expand, expandedKeys) { - console.log('onExpand', expand, expandedKeys); - }, onSelect(info) { console.log('selected', info); }, @@ -35,12 +39,12 @@ const Demo = React.createClass({ }, render() { return ( - + onSelect={this.onSelect} onCheck={this.onCheck} + > diff --git a/components/tree/demo/draggable.md b/components/tree/demo/draggable.md index 8fd19320cc..8a1649f1db 100644 --- a/components/tree/demo/draggable.md +++ b/components/tree/demo/draggable.md @@ -1,10 +1,17 @@ -# 拖动示例 +--- +order: 2 +title: + zh-CN: 拖动示例 + en-US: draggable +--- -- order: 2 +## zh-CN 将节点拖拽到其他节点内部或前后。 ---- +## en-US + +Drag treeNode to insert after the other treeNode or insert into the other parent TreeNode. ````jsx import { Tree } from 'antd'; @@ -30,10 +37,10 @@ const generateData = (_level, _preKey, _tns) => { if (_level < 0) { return tns; } - const __level = _level - 1; + const level = _level - 1; children.forEach((key, index) => { tns[index].children = []; - return generateData(__level, key, tns[index].children); + return generateData(level, key, tns[index].children); }); }; generateData(z); @@ -94,15 +101,18 @@ const Demo = React.createClass({ }, render() { const loop = data => data.map((item) => { - if (item.children) { + if (item.children && item.children.length) { return {loop(item.children)}; } return ; }); return ( - + onDrop={this.onDrop} + > {loop(this.state.gData)} ); diff --git a/components/tree/demo/dynamic.md b/components/tree/demo/dynamic.md index 40f94d1ce7..6ac0623505 100644 --- a/components/tree/demo/dynamic.md +++ b/components/tree/demo/dynamic.md @@ -1,10 +1,17 @@ -# 异步数据加载 +--- +order: 3 +title: + zh-CN: 异步数据加载 + en-US: load data asynchronously +--- -- order: 3 +## zh-CN 点击展开节点,动态加载数据。 ---- +## en-US + +To load data asynchronously when click to expand a treeNode. ````jsx import { Tree } from 'antd'; @@ -81,7 +88,7 @@ const Demo = React.createClass({ getNewTreeData(treeData, treeNode.props.eventKey, generateTreeNodes(treeNode), 2); this.setState({ treeData }); resolve(); - }, 500); + }, 1000); }); }, render() { diff --git a/components/tree/index.en-US.md b/components/tree/index.en-US.md new file mode 100644 index 0000000000..2779960a0f --- /dev/null +++ b/components/tree/index.en-US.md @@ -0,0 +1,57 @@ +--- +category: Components +type: Views +title: Tree +--- + +## When to use + +Directory, organization, biological classification, country, and etc. Almost things of the world are tree structrue. The `Tree` component is a way of representing the hierachical relationship of these things,and you also can expand, collapse, select the treeNodes of it. + +## API + +### Tree props + +| Property | Description | Type | Default | +|----------------|--------------------------------------------------|------------|---------| +|multiple | Whether allow to multiple select treeNode | bool | false | +|checkable | Whether support checkable treeNode | bool | false | +|defaultExpandAll | Whether default to expand all treeNodes | bool | false | +|defaultExpandedKeys | Specify keys of default expanded treeNodes | String[] | [] | +|expandedKeys |(controlled) Sepcifies keys of expanded treeNodes | String[] | [] | +|autoExpandParent | Whether to automatically expand a parent treeNode | bool | true | +|defaultCheckedKeys | Specifies keys of default checked treeNodes | String[] | [] | +|checkedKeys |(controlled) Specifies keys of checked treeNodes(PS: When specifies a key of treeNode which is a parent treeNode, all children treeNodes of its will be checked; And vice versa, when specifies a key of treeNode which is a child treeNode, its parent treeNode will also be checked. When `checkable` and `checkStrictly` is true, it'a object has `checked` and `halfChecked` property, and no matter child treeNode or parent treeNode is checked, they won't impact on eachother. | String[]/{checked:Array,halfChecked:Array} | [] | +|checkStrictly| Check treeNode precisely, parent treeNode and children treeNodes are not associated | bool | false | +|defaultSelectedKeys | Specifies keys of default selected treeNodes | String[] | [] | +|selectedKeys |(controlled) Specifies keys of selected treeNode | String[] | - | +|onExpand | Defines a function will be called when expand or collapse a treeNode | function(expandedKeys, {expanded: bool, node}) | - | +|onCheck | Defines a function will be called when the onCheck event occurs | function(checkedKeys, e:{checked: bool, checkedNodes, node, event}) | - | +|onSelect | The callback will be invoked when the user clicks a treeNode | function(selectedKeys, e:{selected: bool, selectedNodes, node, event}) | - | +|filterTreeNode | Defines a function to filter treeNodes(highlight),when return true, corresponding treeNode will be highlight | function(node) | - | +|loadData | load data asynchronously | function(node)| - | +|onRightClick | The call back will be invoked when the user right clicks a treeNoe | function({event,node}) | - | +|draggable | Specifies whether this Tree is draggable(IE>8) | bool | false | +|onDragStart | Defines a function will be called when the onDragStart event occurs | function({event,node}) | - | +|onDragEnter | Defines a function will be called when the onDragEnter event occurs | function({event,node,expandedKeys}) | - | +|onDragOver | Defines a function will be called when the onDragOver event occurs | function({event,node}) | - | +|onDragLeave | Defines a function will be called when the onDragLeave event occurs | function({event,node}) | - | +|onDrop | Defines a function will be called when the onDrop event occurs | function({event, node, dragNode, dragNodesKeys}) | - | + +### TreeNode props + +| Property | Description | Type | Default | +|-----------|------------------------------------------|---------|---------| +|disabled | whether disabled the treeNode | bool | false | +|disableCheckbox | whether disable the checkbox of treeNode | bool | false | +|title | title | String/element | '---' | +|key | it's used with (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys. P.S.: it must be unique in all of treeNodes of the tree! | String | internal calculated position of treeNode | +|isLeaf | whether it's leaf node | bool | false | + +## note + +The number of treeNodes can be very large, but when enable `checkable`, +it will spend more computing time, so we cached some calculations(e.g. `this.treeNodesStates`), +to avoid double computing. But, this bring some restrictions, +**when you async load treeNodes, you should render tree like this** +`{this.state.treeData.length ? {this.state.treeData.map(t => )} : 'loading tree'}` diff --git a/components/tree/index.jsx b/components/tree/index.jsx deleted file mode 100644 index eea4b1e63a..0000000000 --- a/components/tree/index.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import Tree from 'rc-tree'; -import animation from '../common/openAnimation'; - -const AntTree = React.createClass({ - getDefaultProps() { - return { - prefixCls: 'ant-tree', - checkable: false, - showIcon: false, - openAnimation: animation, - }; - }, - render() { - const props = this.props; - let checkable = props.checkable; - if (checkable) { - checkable = ; - } - return ( - - {this.props.children} - - ); - } -}); - -AntTree.TreeNode = Tree.TreeNode; -export default AntTree; diff --git a/components/tree/index.tsx b/components/tree/index.tsx new file mode 100644 index 0000000000..0c176acb8e --- /dev/null +++ b/components/tree/index.tsx @@ -0,0 +1,103 @@ +import * as React from 'react'; +import RcTree from 'rc-tree'; +import animation from '../_util/openAnimation'; + +export interface TreeNodeProps { + disabled?: boolean; + disableCheckbox?: boolean; + title?: string | React.ReactNode; + key?: string; + isLeaf?: boolean; +} + +export class TreeNode extends React.Component { +} + +export interface TreeNodeEvent { + event: 'check' | 'select'; + node: TreeNode; + checked?: boolean; + checkedNodes?: Array; + selected?: boolean; + selectedNodes?: Array; +} + +export interface TreeNodeMouseEvent { + node: TreeNode; + event: React.MouseEventHandler; +} + +export interface TreeProps { + showLine?: boolean; + className?: string; + /** 是否支持多选 */ + multiple?: boolean; + /**是否自动展开父节点 */ + autoExpandParent?: boolean; + /**checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/ + checkStrictly?: boolean; + /** 是否支持选中 */ + checkable?: boolean; + /** 默认展开所有树节点 */ + defaultExpandAll?: boolean; + /** 默认展开指定的树节点 */ + defaultExpandedKeys?: Array; + /** (受控)展开指定的树节点 */ + expandedKeys?: Array; + /** (受控)选中复选框的树节点 */ + checkedKeys?: Array; + /** 默认选中复选框的树节点 */ + defaultCheckedKeys?: Array; + /** (受控)设置选中的树节点 */ + selectedKeys?: Array; + /** 默认选中的树节点 */ + defaultSelectedKeys?: Array; + /** 展开/收起节点时触发 */ + onExpand?: (expandedKeys: Array, info: { node: TreeNode, expanded: boolean }) => void | PromiseLike; + /** 点击复选框触发 */ + onCheck?: (checkedKeys: Array, e: TreeNodeEvent) => void; + /** 点击树节点触发 */ + onSelect?: (selectedKeys: Array, e: TreeNodeEvent) => void; + /** filter some treeNodes as you need. it should return true */ + filterTreeNode?: (node: TreeNode) => boolean; + /** 异步加载数据 */ + loadData?: (node: TreeNode) => PromiseLike; + /** 响应右键点击 */ + onRightClick?: (options: TreeNodeMouseEvent) => void; + /** 设置节点可拖拽(IE>8)*/ + draggable?: boolean; + /** 开始拖拽时调用 */ + onDragStart?: (options: TreeNodeMouseEvent) => void; + /** dragenter 触发时调用 */ + onDragEnter?: (options: TreeNodeMouseEvent) => void; + /** dragover 触发时调用 */ + onDragOver?: (options: TreeNodeMouseEvent) => void; + /** dragleave 触发时调用 */ + onDragLeave?: (options: TreeNodeMouseEvent) => void; + /** drop 触发时调用 */ + onDrop?: (options: TreeNodeMouseEvent) => void; + style?: React.CSSProperties; + prefixCls?: string; +} + +export default class Tree extends React.Component { + static TreeNode = RcTree.TreeNode; + + static defaultProps = { + prefixCls: 'ant-tree', + checkable: false, + showIcon: false, + openAnimation: animation, + }; + + render() { + const props = this.props; + let checkable = props.checkable; + return ( + ) : checkable }> + {this.props.children} + + ); + } +} diff --git a/components/tree/index.md b/components/tree/index.zh-CN.md similarity index 63% rename from components/tree/index.md rename to components/tree/index.zh-CN.md index 33e1939b56..5dab0d667e 100644 --- a/components/tree/index.md +++ b/components/tree/index.zh-CN.md @@ -1,9 +1,8 @@ -# Tree - -- category: Components -- chinese: 树形控件 -- type: 展示 - +--- +category: Components +type: Views +title: Tree +subtitle: 树形控件 --- ## 何时使用 @@ -21,14 +20,16 @@ |defaultExpandAll | 默认展开所有树节点 | bool | false | |defaultExpandedKeys | 默认展开指定的树节点 | String[] | [] | |expandedKeys | (受控)展开指定的树节点 | String[] | [] | -|checkedKeys | (受控)选中复选框的树节点 | String[] | [] | +|autoExpandParent | 是否自动展开父节点 | bool | true | |defaultCheckedKeys | 默认选中复选框的树节点 | String[] | [] | -|selectedKeys | (受控)设置选中的树节点 | String[] | - | +|checkedKeys | (受控)选中复选框的树节点(注意:父子节点有关联,如果传入父节点key,则子节点自动选中;相应当子节点key都传入,父节点也自动选中。当设置`checkable`和`checkStrictly`,它是一个有`checked`和`halfChecked`属性的对象,并且父子节点的选中与否不再关联 | String[]/{checked:Array,halfChecked:Array} | [] | +|checkStrictly| checkable状态下节点选择完全受控(父子节点选中状态不再关联)| bool | false | |defaultSelectedKeys | 默认选中的树节点 | String[] | [] | -|onExpand | 展开/收起节点时触发 | function(node, expanded, expandedKeys) | - | +|selectedKeys | (受控)设置选中的树节点 | String[] | - | +|onExpand | 展开/收起节点时触发 | function(expandedKeys, {expanded: bool, node}) | - | |onCheck | 点击复选框触发 | function(checkedKeys, e:{checked: bool, checkedNodes, node, event}) | - | |onSelect | 点击树节点触发 | function(selectedKeys, e:{selected: bool, selectedNodes, node, event}) | - | -|filterTreeNode | filter some treeNodes as you need. it should return true | function(node) | - | +|filterTreeNode | 按需筛选树节点(高亮),返回true | function(node) | - | |loadData | 异步加载数据 | function(node)| - | |onRightClick | 响应右键点击 | function({event,node}) | - | |draggable | 设置节点可拖拽(IE>8) | bool | false | @@ -44,6 +45,10 @@ |-----------|------------------------------------------|------------|--------| |disabled | 禁掉响应 | bool | false | |disableCheckbox | 禁掉 checkbox | bool | false | -|title | 标题 | String | '---' | +|title | 标题 | String/element | '---' | |key | 被树的 (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys 属性所用。注意:整个树范围内的所有节点的 key 值不能重复! | String | 内部计算出的节点位置 | |isLeaf | 设置为叶子节点 | bool | false | + +## 注意 + +树节点可以有很多,但在设置`checkable`时、将会花费更多的计算时间,因此我们缓存了一些计算结果(像`this.treeNodesStates`)来复用、避免多次重复计算、以此提高性能。但这也带来了一些限制,当你异步加载树节点时,你需要这样渲染树:`{this.state.treeData.length ? {this.state.treeData.map(t => )} : 'loading tree'}` diff --git a/style/components/tree.less b/components/tree/style/index.less similarity index 85% rename from style/components/tree.less rename to components/tree/style/index.less index cc4585be8b..f3b4cea514 100644 --- a/style/components/tree.less +++ b/components/tree/style/index.less @@ -1,21 +1,11 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; +@import "../../checkbox/style/mixin"; +@import "./mixin"; + @tree-prefix-cls: ant-tree; .antCheckboxFn(@checkbox-prefix-cls: ant-tree-checkbox); -@import "../mixins/iconfont"; -.antTreeSwitcherIcon() { - position: relative; - &:after { - .iconfont-size-under-12px(6px); - content: '\e611'; - display: inline-block; - font-family: 'anticon'; - font-weight: bold; - position: absolute; - top: 0; - right: 4px; - color: #666; - transition: transform .3s ease; - } -} + .@{tree-prefix-cls} { margin: 0; padding: 5px; @@ -98,15 +88,16 @@ } &.@{tree-prefix-cls}-icon_loading { &:after { - content: '\e6a1'; display: inline-block; - font-family: 'anticon'; - font-weight: bold; - .animation(loadingCircle 1s infinite linear); - margin-top: 8px; + .iconfont-font("\e6a1"); + animation: loadingCircle 1s infinite linear; + color: @primary-color; } } &.@{tree-prefix-cls}-switcher { + &.@{tree-prefix-cls}-switcher-noop { + cursor: auto; + } &.@{tree-prefix-cls}-roots_open, &.@{tree-prefix-cls}-center_open, &.@{tree-prefix-cls}-bottom_open, diff --git a/components/tree/style/index.tsx b/components/tree/style/index.tsx new file mode 100644 index 0000000000..68627a2a99 --- /dev/null +++ b/components/tree/style/index.tsx @@ -0,0 +1,5 @@ +import '../../style/index.less'; +import './index.less'; + +// style dependencies +import '../../checkbox/style'; diff --git a/components/tree/style/mixin.less b/components/tree/style/mixin.less new file mode 100644 index 0000000000..8cd440fb57 --- /dev/null +++ b/components/tree/style/mixin.less @@ -0,0 +1,16 @@ +@import "../../style/mixins/index"; + +.antTreeSwitcherIcon() { + position: relative; + &:after { + .iconfont-size-under-12px(6px); + display: inline-block; + .iconfont-font("\e611"); + font-weight: bold; + position: absolute; + top: 1px; + right: 4px; + color: #666; + transition: transform .3s ease; + } +} diff --git a/components/upload/demo/basic.md b/components/upload/demo/basic.md index ab4dfbbd1d..93c38b7484 100644 --- a/components/upload/demo/basic.md +++ b/components/upload/demo/basic.md @@ -1,17 +1,19 @@ -# 点击上传 - -- order: 0 +--- +order: 0 +title: 点击上传 +--- 经典款式,用户点击按钮弹出文件选择框。 ---- - ````jsx import { Upload, message, Button, Icon } from 'antd'; const props = { name: 'file', action: '/upload.do', + headers: { + authorization: 'authorization-text', + }, onChange(info) { if (info.file.status !== 'uploading') { console.log(info.file, info.fileList); @@ -21,7 +23,7 @@ const props = { } else if (info.file.status === 'error') { message.error(`${info.file.name} 上传失败。`); } - } + }, }; ReactDOM.render( diff --git a/components/upload/demo/beforeUpload.md b/components/upload/demo/beforeUpload.md index 1f09508322..ca06510fae 100644 --- a/components/upload/demo/beforeUpload.md +++ b/components/upload/demo/beforeUpload.md @@ -1,10 +1,11 @@ -# 限制用户上传的文件 - -- order: 7 +--- +order: 7 +title: 限制用户上传的文件 +--- 可以通过 `beforeUpload` 在文件上传之前进行干预,如限制用户只能上传 JPG 文件。 ---- +也支持异步检查,`beforeUpload` 的返回值可以是一个 Promise:[示例](http://react-component.github.io/upload/examples/beforeUpload.html)。 ````jsx import { Upload, Button, Icon, message } from 'antd'; @@ -17,7 +18,7 @@ const props = { message.error('只能上传 JPG 文件哦!'); } return isJPG; - } + }, }; ReactDOM.render( diff --git a/components/upload/demo/defaultFileList.md b/components/upload/demo/defaultFileList.md index ba3bd5762f..f9a8d9610e 100644 --- a/components/upload/demo/defaultFileList.md +++ b/components/upload/demo/defaultFileList.md @@ -1,11 +1,10 @@ -# 传入已上传的文件 - -- order: 1 +--- +order: 1 +title: 传入已上传的文件 +--- 对已上传的文件进行编辑。 ---- - ````jsx import { Upload, Button, Icon } from 'antd'; @@ -21,13 +20,13 @@ const props = { uid: -1, name: 'xxx.png', status: 'done', - url: 'http://www.baidu.com/xxx.png' + url: 'http://www.baidu.com/xxx.png', }, { uid: -2, name: 'yyy.png', status: 'done', - url: 'http://www.baidu.com/yyy.png' - }] + url: 'http://www.baidu.com/yyy.png', + }], }; ReactDOM.render( diff --git a/components/upload/demo/drag.md b/components/upload/demo/drag.md index 5932e07df6..2a562a3205 100644 --- a/components/upload/demo/drag.md +++ b/components/upload/demo/drag.md @@ -1,37 +1,46 @@ -# 拖拽上传 - -- order: 3 +--- +order: 3 +title: 拖拽上传 +--- 可以把文件拖入指定区域,完成上传,同样支持点击上传。 ---- - ````jsx -import { Upload, Icon } from 'antd'; +import { Upload, Icon, message } from 'antd'; const Dragger = Upload.Dragger; const props = { name: 'file', showUploadList: false, action: '/upload.do', + onChange(info) { + if (info.file.status !== 'uploading') { + console.log(info.file, info.fileList); + } + if (info.file.status === 'done') { + message.success(`${info.file.name} 上传成功。`); + } else if (info.file.status === 'error') { + message.error(`${info.file.name} 上传失败。`); + } + }, }; ReactDOM.render( -
    -
    - - - +
    +
    + + + +
    +
    + +

    + +

    +

    点击或将文件拖拽到此区域上传

    +

    支持单个或批量上传,严禁上传公司内部资料及其他违禁文件

    +
    +
    -
    - -

    - -

    -

    点击或将文件拖拽到此区域上传

    -

    支持单个或批量上传,严禁上传公司内部资料及其他违禁文件

    -
    -
    -
    , mountNode); ```` diff --git a/components/upload/demo/fileList.md b/components/upload/demo/fileList.md index af4f0552c5..3df1ce3224 100644 --- a/components/upload/demo/fileList.md +++ b/components/upload/demo/fileList.md @@ -1,6 +1,7 @@ -# 完全控制的上传列表 - -- order: 2 +--- +order: 2 +title: 完全控制的上传列表 +--- 使用 `fileList` 对列表进行完全控制,可以实现各种自定义功能,以下演示三种情况: @@ -10,8 +11,6 @@ 3) 按照服务器返回信息筛选成功上传的文件。 ---- - ````jsx import { Upload, Button, Icon } from 'antd'; @@ -22,8 +21,8 @@ const MyUpload = React.createClass({ uid: -1, name: 'xxx.png', status: 'done', - url: 'http://www.baidu.com/xxx.png' - }] + url: 'http://www.baidu.com/xxx.png', + }], }; }, handleChange(info) { @@ -56,7 +55,7 @@ const MyUpload = React.createClass({ const props = { action: '/upload.do', onChange: this.handleChange, - multiple: true + multiple: true, }; return ( @@ -65,7 +64,7 @@ const MyUpload = React.createClass({ ); - } + }, }); ReactDOM.render(, mountNode); diff --git a/components/upload/demo/multiple.md b/components/upload/demo/multiple.md index 3538ac32cc..be410c9616 100644 --- a/components/upload/demo/multiple.md +++ b/components/upload/demo/multiple.md @@ -1,12 +1,11 @@ -# 多文件选择 - -- order: 5 -- hidden: 5 +--- +order: 5 +hidden: true +title: 多文件选择 +--- 按住 ctrl 可选择多个文件,`ie10+` 支持。 ---- - ````jsx import { Upload, message, Button, Icon } from 'antd'; @@ -22,7 +21,7 @@ const props = { } else if (info.file.status === 'error') { message.error(`${info.file.name} 上传失败。`); } - } + }, }; ReactDOM.render( diff --git a/components/upload/demo/picture-card.md b/components/upload/demo/picture-card.md index a4bcc5c8e1..03f280bd2c 100644 --- a/components/upload/demo/picture-card.md +++ b/components/upload/demo/picture-card.md @@ -1,38 +1,62 @@ -# 图片卡片样式 - -- order: 8 +--- +order: 8 +title: 图片卡片样式 +--- 上传文件为图片,可展示本地缩略图。 ---- - ````jsx -import { Upload, Icon } from 'antd'; +import { Upload, Icon, Modal } from 'antd'; -const props = { - action: '/upload.do', - listType: 'picture-card', - defaultFileList: [{ - uid: -1, - name: 'xxx.png', - status: 'done', - url: 'https://os.alipayobjects.com/rmsportal/NDbkJhpzmLxtPhB.png', - thumbUrl: 'https://os.alipayobjects.com/rmsportal/NDbkJhpzmLxtPhB.png', - }] -}; +const ImageUploadList = React.createClass({ + getInitialState() { + return { + previewVisible: false, + previewImage: '', + }; + }, + handleCancel() { + this.setState({ + previewVisible: false, + }); + }, + render() { + const props = { + action: '/upload.do', + listType: 'picture-card', + defaultFileList: [{ + uid: -1, + name: 'xxx.png', + status: 'done', + url: 'https://os.alipayobjects.com/rmsportal/NDbkJhpzmLxtPhB.png', + thumbUrl: 'https://os.alipayobjects.com/rmsportal/NDbkJhpzmLxtPhB.png', + }], + onPreview: (file) => { + this.setState({ + previewImage: file.url, + previewVisible: true, + }); + }, + }; + return ( +
    + + +
    上传照片
    +
    + + example + 示例 + + + example + +
    + ); + }, +}); -ReactDOM.render( -
    - - -
    上传照片
    -
    - - - 示例 - -
    -, mountNode); +ReactDOM.render(, mountNode); ```` ````css diff --git a/components/upload/demo/picture-style.md b/components/upload/demo/picture-style.md index 0d2259dc24..95bcde1d01 100644 --- a/components/upload/demo/picture-style.md +++ b/components/upload/demo/picture-style.md @@ -1,13 +1,12 @@ -# 图片列表样式 - -- order: 6 +--- +order: 6 +title: 图片列表样式 +--- 上传文件为图片,可展示本地缩略图。 `IE8/9` 不支持浏览器本地缩略图展示([Ref](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL)),可以写 `thumbUrl` 属性来代替。 ---- - ````jsx import { Upload, Button, Icon } from 'antd'; @@ -26,24 +25,24 @@ const props = { status: 'done', url: 'https://os.alipayobjects.com/rmsportal/NDbkJhpzmLxtPhB.png', thumbUrl: 'https://os.alipayobjects.com/rmsportal/NDbkJhpzmLxtPhB.png', - }] + }], }; ReactDOM.render( -
    - - - -
    -
    - - - -
    +
    + + + +
    +
    + + + +
    , mountNode); ```` diff --git a/components/upload/getFileItem.js b/components/upload/getFileItem.tsx similarity index 100% rename from components/upload/getFileItem.js rename to components/upload/getFileItem.tsx diff --git a/components/upload/index.md b/components/upload/index.md index 6fba743b8f..5d7d71174e 100644 --- a/components/upload/index.md +++ b/components/upload/index.md @@ -1,9 +1,8 @@ -# Upload - -- category: Components -- chinese: 文件上传 -- type: 表单 - +--- +category: Components +chinese: 上传 +type: Form Controls +english: Upload --- 文件选择上传和拖拽上传控件。 @@ -21,27 +20,33 @@ | 参数 | 说明 | 类型 | 默认值| |------------|--------------------------------------------------------------| ----------- |-------| | name | 可选参数, 上传的文件 | String | file | +| defaultFileList | 可选参数,默认已经上传的文件列表 | Array[Object] | 无 | +| fileList | 可选参数,已经上传的文件列表 | Array[Object] | 无 | | action | 必选参数, 上传的地址 | String | 无 | -| data | 可选参数, 上传所需参数 | Object | 无 | +| data | 可选参数, 上传所需参数或返回上传参数的方法 | Object or function(file) | 无 | | headers | 可选参数, 设置上传的请求头部,IE10 以上有效 | Object | 无 | | showUploadList | 可选参数, 是否展示 uploadList, 默认开启 | Boolean | true | | multiple | 可选参数, 是否支持多选文件,`ie10+` 支持。开启后按住 ctrl 可选择多个文件。 | Boolean | false | -| accept | 可选参数, 接受上传的文件类型, 详见 input accept Attribute | String | 无 | +| accept | 可选参数, 接受上传的文件类型, 详见 [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept) | String | 无 | | beforeUpload | 可选参数, 上传文件之前的钩子,参数为上传的文件,若返回 `false` 或者 Promise 则停止上传。**注意:该方法不支持老 IE**。 | Function | 无 | | onChange | 可选参数, 上传文件改变时的状态,详见 onChange | Function | 无 | | listType | 上传列表的内建样式,支持两种基本样式 `text` or `picture` | String | 'text'| -| className | 自定义类名 | String | 无 | - +| onPreview | 点击文件链接时的回调 | Function(file) | 无 | +| onRemove | 点击移除文件时的回调 | Function(file) | 无 | +| supportServerRender | 服务端渲染时需要打开这个 | Boolean | false | +| disabled | 是否禁用 | Boolean | false | ### onChange +> 上传中、完成、失败都会调用这个函数。 + 文件状态改变的回调,返回为: ```js { - file: { ... }, - fileList: [ ... ], - event: { ... } + file: { /* ... */ }, + fileList: [ /* ... */ ], + event: { /* ... */ }, } ``` @@ -52,7 +57,7 @@ uid: 'uid', // 文件唯一标识,建议设置为负数,防止和内部产生的 id 冲突 name: 'xx.png' // 文件名 status: 'done', // 状态有:uploading done error removed - response: '{"status":"success"}' // 服务端响应内容 + response: '{"status": "success"}', // 服务端响应内容 } ``` diff --git a/components/upload/index.jsx b/components/upload/index.tsx similarity index 55% rename from components/upload/index.jsx rename to components/upload/index.tsx index e3079bf154..4996f5db83 100644 --- a/components/upload/index.jsx +++ b/components/upload/index.tsx @@ -1,10 +1,11 @@ -import React from 'react'; -import Upload from 'rc-upload'; -import assign from 'object-assign'; +import * as React from 'react'; +import RcUpload from 'rc-upload'; import UploadList from './uploadList'; import getFileItem from './getFileItem'; import classNames from 'classnames'; const prefixCls = 'ant-upload'; +import assign from 'object-assign'; +import { UploadProps } from './interface'; function noop() { } @@ -27,6 +28,7 @@ function fileToObject(file) { error: file.error, percent: 0, originFileObj: file, + status: null, }; } @@ -53,16 +55,50 @@ function genPercentAdd() { }; } -const AntUpload = React.createClass({ - getInitialState() { - return { - fileList: this.props.fileList || this.props.defaultFileList || [], - dragState: 'drop' - }; - }, +export { UploadProps }; - onStart(file) { - if (this.recentUploadStatus === false) return; +export function Dragger(props) { + return ; +} + +export default class Upload extends React.Component { + static Dragger = Dragger; + + static defaultProps = { + prefixCls: 'ant-upload-btn', + type: 'select', + // do not set + // name: '', + multiple: false, + action: '', + data: {}, + accept: '', + onChange: noop, + beforeUpload: T, + showUploadList: true, + listType: 'text', // or pictrue + className: '', + disabled: false, + }; + + recentUploadStatus: boolean | PromiseLike; + progressTimer: any; + refs: { + upload: any; + }; + + constructor(props) { + super(props); + this.state = { + fileList: this.props.fileList || this.props.defaultFileList || [], + dragState: 'drop', + }; + } + + onStart = (file) => { + if (this.recentUploadStatus === false) { + return; + } let targetItem; let nextFileList = this.state.fileList.concat(); @@ -80,13 +116,13 @@ const AntUpload = React.createClass({ } this.onChange({ file: targetItem, - fileList: nextFileList + fileList: nextFileList, }); // fix ie progress - if (!window.FormData) { + if (!(window as any).FormData) { this.autoUpdateProgress(0, targetItem); } - }, + } autoUpdateProgress(percent, file) { const getPercent = genPercentAdd(); @@ -94,10 +130,10 @@ const AntUpload = React.createClass({ this.progressTimer = setInterval(() => { curPercent = getPercent(curPercent); this.onProgress({ - percent: curPercent + percent: curPercent, }, file); }, 200); - }, + } removeFile(file) { let fileList = this.state.fileList; @@ -108,59 +144,63 @@ const AntUpload = React.createClass({ return fileList; } return null; - }, + } - onSuccess(response, file) { + onSuccess = (response, file) => { this.clearProgressTimer(); - // 服务器端需要返回标准 json 字符串 - // 否则视为失败 try { if (typeof response === 'string') { - JSON.parse(response); + response = JSON.parse(response); } - } catch (e) { - this.onError(new Error('No response'), response, file); + } catch (e) { /* do nothing */ + } + let fileList = this.state.fileList; + let targetItem = getFileItem(file, fileList); + // removed + if (!targetItem) { return; } - let fileList = this.state.fileList; - let targetItem = getFileItem(file, fileList); - // 之前已经删除 - if (targetItem) { - targetItem.status = 'done'; - targetItem.response = response; - this.onChange({ - file: targetItem, - fileList - }); - } - }, + targetItem.status = 'done'; + targetItem.response = response; + this.onChange({ + file: targetItem, + fileList, + }); + } - onProgress(e, file) { + onProgress = (e, file) => { let fileList = this.state.fileList; let targetItem = getFileItem(file, fileList); - if (!targetItem) return; + // removed + if (!targetItem) { + return; + } targetItem.percent = e.percent; this.onChange({ event: e, - file, - fileList: this.state.fileList + file: targetItem, + fileList: this.state.fileList, }); - }, + } - onError(error, response, file) { + onError = (error, response, file) => { this.clearProgressTimer(); let fileList = this.state.fileList; let targetItem = getFileItem(file, fileList); + // removed + if (!targetItem) { + return; + } targetItem.error = error; targetItem.response = response; targetItem.status = 'error'; this.handleRemove(targetItem); - }, + } - beforeUpload(file) { + beforeUpload = (file) => { this.recentUploadStatus = this.props.beforeUpload(file); return this.recentUploadStatus; - }, + } handleRemove(file) { let fileList = this.removeFile(file); @@ -170,38 +210,24 @@ const AntUpload = React.createClass({ fileList, }); } - }, + } - handleManualRemove(file) { - /*eslint-disable */ - file.status = 'removed'; - /*eslint-enable */ - this.handleRemove(file); - }, + handleManualRemove = (file) => { + this.refs.upload.abort(file); + file.status = 'removed'; // eslint-disable-line + if ('onRemove' in this.props) { + this.props.onRemove(file); + } else { + this.handleRemove(file); + } + } - onChange(info) { - this.setState({ - fileList: info.fileList - }); + onChange = (info) => { + if (!('fileList' in this.props)) { + this.setState({ fileList: info.fileList }); + } this.props.onChange(info); - }, - - getDefaultProps() { - return { - type: 'select', - // do not set - // name: '', - multiple: false, - action: '', - data: {}, - accept: '', - onChange: noop, - beforeUpload: T, - showUploadList: true, - listType: 'text', // or pictrue - className: '', - }; - }, + } componentWillReceiveProps(nextProps) { if ('fileList' in nextProps) { @@ -209,17 +235,17 @@ const AntUpload = React.createClass({ fileList: nextProps.fileList || [], }); } - }, + } - onFileDrop(e) { + onFileDrop = (e) => { this.setState({ - dragState: e.type + dragState: e.type, }); - }, + } clearProgressTimer() { clearInterval(this.progressTimer); - }, + } render() { let type = this.props.type || 'select'; @@ -233,67 +259,67 @@ const AntUpload = React.createClass({ let uploadList; if (this.props.showUploadList) { uploadList = ( - + onPreview={props.onPreview} + onRemove={this.handleManualRemove} + /> ); } if (type === 'drag') { - let dragUploadingClass = this.state.fileList.some(file => file.status === 'uploading') - ? `${prefixCls}-drag-uploading` : ''; - let draggingClass = this.state.dragState === 'dragover' - ? `${prefixCls}-drag-hover` : ''; + const dragCls = classNames({ + [prefixCls]: true, + [`${prefixCls}-drag`]: true, + [`${prefixCls}-drag-uploading`]: this.state.fileList.some(file => file.status === 'uploading'), + [`${prefixCls}-drag-hover`]: this.state.dragState === 'dragover', + [`${prefixCls}-disabled`]: this.props.disabled, + }); return ( -
    - + onDragLeave={this.onFileDrop} + > +
    {this.props.children}
    -
    -
    - {uploadList} -
    - ); - } else if (type === 'select') { - const uploadButtonCls = classNames({ - [prefixCls]: true, - [`${prefixCls}-select`]: true, - [`${prefixCls}-select-${this.props.listType}`]: true, - }); - if (this.props.listType === 'picture-card') { - return ( - - {uploadList} -
    - - {this.props.children} - -
    -
    - ); - } - return ( - -
    - - {this.props.children} - +
    {uploadList}
    ); } - } -}); -AntUpload.Dragger = React.createClass({ - render() { - return ; - } -}); + const uploadButtonCls = classNames({ + [prefixCls]: true, + [`${prefixCls}-select`]: true, + [`${prefixCls}-select-${this.props.listType}`]: true, + [`${prefixCls}-disabled`]: this.props.disabled, + }); -export default AntUpload; + const uploadButton = this.props.children + ?
    + : null; + + const className = this.props.className; + + if (this.props.listType === 'picture-card') { + return ( + + {uploadList} + {uploadButton} + + ); + } + + return ( + + {uploadButton} + {uploadList} + + ); + } +} diff --git a/components/upload/interface.tsx b/components/upload/interface.tsx new file mode 100644 index 0000000000..902a077e1a --- /dev/null +++ b/components/upload/interface.tsx @@ -0,0 +1,55 @@ +import * as React from 'react'; + +type UploadFileStatus = 'error' | 'success' | 'done' | 'uploading' | 'removed' + +export interface HttpRequestHeader { + [key: string]: string; +} + +export interface File { + uid: number; + size: number; + name: string; + lastModifiedDate?: Date; + url?: string; + status?: UploadFileStatus; + percent?: number; + thumbUrl?: string; + originFileObj?: File; +} + +interface UploadChangeParam { + file: File; + fileList: Array; + event?: { percent: number }; +} + +export interface UploadProps { + type?: 'drag' | 'select'; + name?: string; + defaultFileList?: Array; + fileList?: Array; + action: string; + data?: Object; + headers?: HttpRequestHeader; + showUploadList?: boolean; + multiple?: boolean; + accept?: string; + beforeUpload?: (file: File) => boolean | PromiseLike; + onChange?: (info: UploadChangeParam) => void; + listType?: 'text' | 'picture' | 'picture-card'; + className?: string; + onPreview?: (file: File) => void; + onRemove?: (file: File) => void; + supportServerRender?: boolean; + style?: React.CSSProperties; + disabled?: boolean; +} + +export interface UploadListProps { + listType?: 'text' | 'picture' | 'picture-card'; + onPreview?: (file: File) => void; + onRemove?: (file: File) => void; + items?: Array; + progressAttr?: Object; +} diff --git a/style/components/upload.less b/components/upload/style/index.less similarity index 91% rename from style/components/upload.less rename to components/upload/style/index.less index 705857270d..c276432fda 100644 --- a/style/components/upload.less +++ b/components/upload/style/index.less @@ -1,11 +1,14 @@ +@import "../../style/themes/default"; +@import "../../style/mixins/index"; + @upload-prefix-cls: ant-upload; @upload-item: ant-upload-list-item; -@pictrue-card-size: 96px; +@upload-pictrue-card-size: 96px; .@{upload-prefix-cls} { font-size: @font-size-base; - > span { + &-btn { display: block; width: 100%; outline: none; @@ -21,8 +24,8 @@ &&-select-picture-card { border: 1px dashed @border-color-base; - width: @pictrue-card-size; - height: @pictrue-card-size; + width: @upload-pictrue-card-size; + height: @upload-pictrue-card-size; padding: 24px 0; border-radius: @border-radius-base; background-color: #fbfbfb; @@ -49,11 +52,15 @@ height: 100%; position: relative; - &.@{upload-prefix-cls}-drag-hover { + &.@{upload-prefix-cls}-drag-hover:not(.@{upload-prefix-cls}-disabled) { border: 2px dashed tint(@primary-color, 20%); } - > span { + &.@{upload-prefix-cls}-disabled { + cursor: not-allowed; + } + + .@{upload-prefix-cls}-btn { display: table; height: 100%; } @@ -63,7 +70,7 @@ vertical-align: middle; } - &:hover { + &:not(.@{upload-prefix-cls}-disabled):hover { border-color: tint(@primary-color, 20%); } @@ -98,6 +105,7 @@ } .@{upload-prefix-cls}-list { + overflow: hidden; &-item { overflow: hidden; margin-top: 8px; @@ -233,12 +241,12 @@ } &-picture-card { - display: inline-block; + display: inline; .@{upload-item} { display: inline-block; - width: @pictrue-card-size; - height: @pictrue-card-size; + width: @upload-pictrue-card-size; + height: @upload-pictrue-card-size; margin: 0 8px 8px 0; } @@ -313,6 +321,7 @@ } .@{upload-item}-info { + height: auto; &:before, .anticon-eye-o, .anticon-delete { diff --git a/components/upload/style/index.tsx b/components/upload/style/index.tsx new file mode 100644 index 0000000000..61e3973fcb --- /dev/null +++ b/components/upload/style/index.tsx @@ -0,0 +1,5 @@ +import '../../style/index.less'; +import './index.less'; + +// style dependencies +import '../../progress/style'; diff --git a/components/upload/uploadList.jsx b/components/upload/uploadList.tsx similarity index 52% rename from components/upload/uploadList.jsx rename to components/upload/uploadList.tsx index 8afe60e951..ca166289c3 100644 --- a/components/upload/uploadList.jsx +++ b/components/upload/uploadList.tsx @@ -1,33 +1,39 @@ -import React from 'react'; +import * as React from 'react'; import Animate from 'rc-animate'; import Icon from '../icon'; const prefixCls = 'ant-upload'; -import { Line } from '../progress'; +import Progress from '../progress'; import classNames from 'classnames'; +import { UploadListProps } from './interface'; // https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL -const previewFile = function (file, callback) { +const previewFile = (file, callback) => { const reader = new FileReader(); - reader.onloadend = function () { - callback(reader.result); - }; + reader.onloadend = () => callback(reader.result); reader.readAsDataURL(file); }; -export default React.createClass({ - getDefaultProps() { - return { - listType: 'text', // or picture - items: [], - progressAttr: { - strokeWidth: 3, - showInfo: false - } - }; - }, - handleClose(file) { +export default class UploadList extends React.Component { + static defaultProps = { + listType: 'text', // or picture + items: [], + progressAttr: { + strokeWidth: 3, + showInfo: false, + }, + }; + + handleClose = (file) => { this.props.onRemove(file); - }, + } + + handlePreview = (file, e) => { + if (this.props.onPreview) { + e.preventDefault(); + return this.props.onPreview(file); + } + } + componentDidUpdate() { if (this.props.listType !== 'picture' && this.props.listType !== 'picture-card') { return; @@ -35,7 +41,7 @@ export default React.createClass({ this.props.items.forEach(file => { if (typeof document === 'undefined' || typeof window === 'undefined' || - !window.FileReader || !window.File || + !(window as any).FileReader || !(window as any).File || !(file.originFileObj instanceof File) || file.thumbUrl !== undefined) { return; @@ -50,7 +56,8 @@ export default React.createClass({ this.forceUpdate(); }); }); - }, + } + render() { let list = this.props.items.map(file => { let progress; @@ -64,9 +71,15 @@ export default React.createClass({ icon = ; } } else { - icon = ({file.name} + icon = ( + this.handlePreview(file, e)} + href={file.url} + target="_blank" + > + {file.name} + ); } } @@ -74,7 +87,7 @@ export default React.createClass({ if (file.status === 'uploading') { progress = (
    - +
    ); } @@ -86,20 +99,44 @@ export default React.createClass({
    {icon} - {file.url - ? {file.name} - : {file.name}} + { + file.url + ? ( + this.handlePreview(file, e)} + > + {file.name} + + ) : ( + this.handlePreview(file, e)} + > + {file.name} + + ) + } { this.props.listType === 'picture-card' && file.status !== 'uploading' ? ( - - + this.handlePreview(file, e)} + > + + + this.handleClose(file)} /> - ) : + ) : this.handleClose(file)} /> }
    - { progress } + {progress}
    ); }); @@ -109,10 +146,10 @@ export default React.createClass({ }); return (
    - + {list}
    ); } -}); +} diff --git a/components/validation/index.jsx b/components/validation/index.jsx deleted file mode 100644 index 198cca8668..0000000000 --- a/components/validation/index.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import Validation from 'rc-form-validation'; -import warning from 'warning'; - -export default class AntValidation extends React.Component { - validate(callback) { - this.refs.validation.validate(callback); - } - - reset() { - this.refs.validation.reset(); - } - - forceValidate(fields, callback) { - this.refs.validation.forceValidate(fields, callback); - } - - render() { - warning(false, '`Validation` is deprecated, please use `Form` which has supported validation after antd@0.12.0.'); - return ; - } -} - -AntValidation.Validator = Validation.Validator; -AntValidation.FieldMixin = Validation.FieldMixin; diff --git a/components/validation/index.tsx b/components/validation/index.tsx new file mode 100644 index 0000000000..c2ce6f081e --- /dev/null +++ b/components/validation/index.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import warning from 'warning'; + +export default class Validation extends React.Component { + componentDidMount() { + warning(false, '`Validation` is removed, please use `Form` which has supported validation after antd@0.12.0,' + + ' or you can just import Validation from \'rc-form-validation\' for compatibility'); + } + render() { + return null; + } +} + +Validation.Validator = () => {}; +Validation.FieldMixin = { + setField() {}, +}; diff --git a/components/validation/style/index.tsx b/components/validation/style/index.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/custom-typings.d.ts b/custom-typings.d.ts new file mode 100644 index 0000000000..6bec462f9c --- /dev/null +++ b/custom-typings.d.ts @@ -0,0 +1,236 @@ +declare module 'classnames' { + export default function({}): string; +} + +declare module 'react-addons-pure-render-mixin' { + const exports: any; + export default exports; +} + +declare module 'gregorian-calendar-format' { + export default function({}): string; +} + +declare module 'gregorian-calendar' { + export default function({}): string; +} + +declare module 'gregorian-calendar/lib/locale/en_US' { + export default {}; +} + +declare module 'gregorian-calendar/lib/locale/zh_CN' { + export default {}; +} + +declare module 'gregorian-calendar/lib/locale/ru_RU' { + export default {}; +} + +declare module 'rc-calendar/lib/locale/en_US' { + export default {}; +} + +declare module 'rc-calendar/lib/locale/zh_CN' { + export default {}; +} + +declare module 'rc-calendar/lib/locale/ru_RU' { + export default {}; +} + +declare module 'rc-calendar/lib/FullCalendar' { + export default function(): any; +} + +declare module 'rc-calendar/lib/RangeCalendar' { + export default function(): any; +} + +declare module 'rc-calendar/lib/Picker' { + export default function(): any; +} + +declare module 'rc-calendar/lib/MonthCalendar' { + export default function(): any; +} + +declare module 'rc-time-picker/lib/module/Panel' { + export default function(): any; +} + +declare module 'rc-time-picker/lib/TimePicker' { + export default function(): any; +} + +declare module 'rc-time-picker/lib/locale/en_US' { + export default {}; +} + +declare module 'rc-time-picker/lib/locale/zh_CN' { + export default {}; +} + +declare module 'rc-time-picker/lib/locale/ru_RU' { + export default {}; +} + +declare module 'rc-pagination/lib/locale/en_US' { + export default {}; +} + +declare module 'rc-pagination/lib/locale/ru_RU' { + export default {}; +} + +declare module "object-assign" { + export default function(target: any, ...sources: any[]): any; +} + +declare module "object.omit" { + export default function(target: any, ...sources: any[]): any; +} + +declare module 'rc-animate' { + export default function(): any; +} + +declare module 'rc-util/lib/Dom/addEventListener' { + export default function(domNode: any, event: string, handler: Function): any; +} + +declare module 'shallowequal' { + export default function(source: any, target: any): boolean; +} + +declare module 'warning' { + export default function(condition: boolean, message: string): void; +} + +declare module 'css-animation' { + export default function(...any): any; +} + +declare module 'rc-select' { + export const Option: any; + export const OptGroup: any; + const exports: any; + export default exports; +} + +declare module 'react-slick' { + export default function(): any; +} + +declare module 'rc-cascader' { + export default function(): any; +} + +declare module 'array-tree-filter' { + export default function(): any; +} + +declare module 'rc-checkbox' { + export default function(): any; +} + +declare module 'rc-radio' { + export default function(): any; +} + +declare module 'rc-dropdown' { + export default function(): any; +} + +declare module 'rc-editor-mention' { + export default function(): any; +} + +declare module 'rc-progress' { + export default function(): any; +} + +declare module 'rc-menu' { + export const SubMenu: any; + export const Item: any; + export default function(): any; +} + +declare module 'rc-tabs' { + export const TabPane: any; + export default function(): any; +} + +declare module 'rc-tree' { + export default function(): any; +} + +declare module 'rc-tooltip' { + export default function(): any; +} + +declare module 'rc-calendar' { + export default function(): any; +} + +declare module 'rc-input-number' { + export default function(): any; +} + +declare module 'rc-pagination' { + export default function(): any; +} + +declare module 'rc-notification' { + export default function(): any; +} + +declare module 'rc-dialog' { + export default function(): any; +} + +declare module 'rc-rate' { + export default function(): any; +} + +declare module 'rc-queue-anim' { + export default function(): any; +} + +declare module 'rc-slider' { + export default function(): any; +} + +declare module 'rc-steps' { + export const exports: any; + export default exports; +} + +declare module 'rc-switch' { + const exports: any; + export default exports; +} + +declare module 'rc-table' { + export default function(): any; +} + +declare module 'rc-tree-select' { + export const TreeNode: any; + export const SHOW_ALL: any; + export const SHOW_PARENT: any; + export const SHOW_CHILD: any; + export default function(): any; +} + +declare module 'rc-upload' { + export default function(): any; +} + +declare module 'rc-collapse' { + export default function(): any; +} + +declare module 'rc-form/lib/createDOMForm' { + export default function(): any; +} diff --git a/docs/pattern/advanced-search.md b/docs/pattern/advanced-search.md index 03cfaa15bd..a6746215f8 100644 --- a/docs/pattern/advanced-search.md +++ b/docs/pattern/advanced-search.md @@ -1,12 +1,13 @@ -# 高级搜索 - -- category: 6 -- order: 6 - +--- +order: 6 +chinese: 高级搜索 +english: Advanced Search --- 借助『高级搜索』,用户可以缩小复杂列表/表格等的展示范围。 +--- + ## 常规型 ### 交互 @@ -53,4 +54,4 @@ 字段型一般会出现在主搜索框底部,适合搜索条件和值都比较少的场景中。 -## 案例(敬请期待) +

    案例(敬请期待)

    diff --git a/docs/pattern/classic.md b/docs/pattern/classic.md new file mode 100644 index 0000000000..15859615f7 --- /dev/null +++ b/docs/pattern/classic.md @@ -0,0 +1,8 @@ +--- +order: 0 +disabled: true +chinese: 典型页面 +english: Classic +--- + +敬请期待。 diff --git a/docs/pattern/complex-table.md b/docs/pattern/complex-table.md index 2c4e103a70..3c73d86873 100644 --- a/docs/pattern/complex-table.md +++ b/docs/pattern/complex-table.md @@ -1,12 +1,13 @@ -# 表格:复杂数据 - -- category: 5 -- order: 5 - +--- +order: 5 +chinese: 表格:复杂数据 +english: Complex Table --- 表格也用于展示复杂和高度结构化数据。 +--- + ## 案例 ### 多列数据 diff --git a/docs/pattern/form.md b/docs/pattern/form.md index ac865a1b5f..8db39546d1 100644 --- a/docs/pattern/form.md +++ b/docs/pattern/form.md @@ -1,8 +1,7 @@ -# 表单 - -- category: 2 -- order: 2 - +--- +order: 2 +chinese: 表单 +english: Form --- 作为获取用户输入的重要交互方式,表单也承担将问题和答案进行配对的角色。 @@ -23,6 +22,8 @@ 4. 不要提出不必要的问题。 +--- + ## 内容 结构示例 @@ -106,7 +107,7 @@ ### 字数校验框 -字数校验框示例 +字数校验框示例 用于统计当前输入长度,以及是否超过系统阈值。 @@ -121,8 +122,8 @@ ### 输入框宽度 -正确示例 -错误示例 +正确示例 +错误示例 当内容可预知,可以根据内容长短进行定义其落在多少个栅格上。 diff --git a/docs/pattern/index.md b/docs/pattern/index.md deleted file mode 100644 index 4f064c76e9..0000000000 --- a/docs/pattern/index.md +++ /dev/null @@ -1,9 +0,0 @@ -# 典型页面 - -- category: 0 -- order: 0 -- disabled: true - ---- - -敬请期待。 diff --git a/docs/pattern/list.md b/docs/pattern/list.md index 2abd1a1d6b..aa0bea14b4 100644 --- a/docs/pattern/list.md +++ b/docs/pattern/list.md @@ -1,8 +1,7 @@ -# 列表 - -- category: 3 -- order: 3 - +--- +order: 3 +chinese: 列表 +english: List --- 列表是非常常见的界面元素,有多种使用场景: @@ -13,11 +12,13 @@ - 排序与过滤 - 重新安排、添加、删除或重新分类列表项 +--- + ## 交互 ### 显示详情信息 -气泡显示示例 +气泡显示示例 气泡显示:用户鼠标点击/悬停某个链接或内容时,在悬浮层上显示该条列表项少量的详情信息。 @@ -60,19 +61,19 @@
    -对等网格示例 +对等网格示例 对等网格:以网格或者矩阵的方式排列内容元素,其中每个元素都有相仿的视觉重量。 ### 显示图片 -走马灯示例 +走马灯示例 走马灯:以一维的形式来显示图片,可用户主动触发或者系统自动播放。
    -缩略图网格示例 +缩略图网格示例 缩略图网格:以二维的形式来展现图片/Icon,具有强烈的视觉效果,可以吸引用户注意。 @@ -98,4 +99,4 @@
    -## 案例(敬请期待) +

    案例(敬请期待)

    diff --git a/docs/pattern/navigation.md b/docs/pattern/navigation.md index 7f2c68132b..2c635d882f 100644 --- a/docs/pattern/navigation.md +++ b/docs/pattern/navigation.md @@ -1,8 +1,7 @@ -# 导航 - -- category: 1 -- order: 1 - +--- +order: 1 +chinese: 导航 +english: Navigation --- 在广义上,任何告知用户他在哪里,他能去什么地方以及如何到达那里的方式,都可以称之为导航。而我们将中后台常见的导航方式进行提炼和封装,帮助设计者快速构建清晰和流畅的系统。当设计者使用导航或者自定义一些导航结构时,请注意: @@ -11,6 +10,8 @@ 2. 保持导航样式和行为一致或者减少导航数量,降低用户学习成本; 3. 尽可能减少页面间的跳转(eg:一个常见任务需要多个页面跳转时,请减少至一至两次),让用户移动距离保持简短。 +--- + ## 常见导航 @@ -31,20 +32,20 @@ - +
    总结一般适用在浏览性强、门户性质的网站,以及一些比较前台化的应用。 适用在操作性强、中后台管理性质的应用。一般适用在浏览性强、门户性质的网站,以及一些比较前台化的应用。
    我们将常见的导航模式分为:侧栏导航和顶部导航,两者各有优缺点,设计者可以根据各自的业务需求进行选择。 -前端实现代码可以参考 [常用布局](/docs/spec/layout#layout-demo-top)。 +前端实现代码可以参考 [常用布局](/docs/spec/layout#docs-spec-layout-demo-top)。 ## 侧栏导航 --- -结构示例 +结构示例 导航的结构由以下几部分组成。 @@ -63,11 +64,11 @@
    -一级类目 +一级类目 -二级类目 +二级类目 -三级类目及以上 +三级类目及以上 我们定义了不同类目层级所对应的导航样式。 @@ -76,7 +77,7 @@ --- -结构示例 +结构示例 导航的结构由以下几部分组成。 @@ -87,10 +88,10 @@
    -一级类目 +一级类目 -二级类目 +二级类目 -三级类目及以上 +三级类目及以上 不同类目层级。 diff --git a/docs/pattern/table.md b/docs/pattern/table.md index 67f56b6ac1..a31a548cf8 100644 --- a/docs/pattern/table.md +++ b/docs/pattern/table.md @@ -1,12 +1,13 @@ -# 表格 - -- category: 4 -- order: 4 - +--- +order: 4 +chinese: 表格 +english: Table --- 表格可被视为一种列表。它经常和其他界面元素一起协同,用于展示和操作结构化数据,并经常用于详情信息的入口。 +--- + ## 内容 结构示例 @@ -138,9 +139,9 @@ ### 列宽 -错误示例 +错误示例 -正确示例 +正确示例 一般是根据栅格来排版,通过设定每一列的宽度比列,来保证一定尺寸之上(eg:1366px)有好的浏览效果。需要注意: @@ -154,4 +155,4 @@ 数值右对齐(带小数则按小数点对齐),其余左对齐。 -## 案例(敬请期待) +

    案例(敬请期待)

    diff --git a/docs/practice/bussiness.md b/docs/practice/bussiness.md index 26e58ecdc8..6f895ed068 100644 --- a/docs/practice/bussiness.md +++ b/docs/practice/bussiness.md @@ -1,9 +1,8 @@ -# 业务组件 - -- category: 1 -- order: 1 -- disabled: true - +--- +order: 1 +disabled: true +chinese: 业务组件 +english: Business --- 占位。 diff --git a/docs/practice/cases.md b/docs/practice/cases.md index 84bc45504c..7d4fd1087a 100644 --- a/docs/practice/cases.md +++ b/docs/practice/cases.md @@ -1,13 +1,12 @@ -# 实践案例 - -- category: 0 -- order: 0 - +--- +order: 0 +chinese: 实践案例 +english: Cases --- -Ant Design 是面向中后台的 UI 设计语言。 +Ant Design 是面向中台的 UI 设计语言。 -从 2015 年 4 月起,Ant Design 在蚂蚁金服中后台产品线迅速推广,对接多条业务线,覆盖系统 40 个以上。定位于中后台业务的 Ant Design 兼顾专业和非专业的设计人员,具有学习成本低、上手速度快、实现效果好等特点,并且提供从界面设计到前端开发的全链路生态,可以大大提升设计和开发的效率。 +从 2015 年 4 月起,Ant Design 在蚂蚁金服中后台产品线迅速推广,对接多条业务线,覆盖系统 40 个以上。定位于中台业务的 Ant Design 兼顾专业和非专业的设计人员,具有学习成本低、上手速度快、实现效果好等特点,并且提供从界面设计到前端开发的全链路生态,可以大大提升设计和开发的效率。 Ant Design 目前在外部也有 [许多产品实践](https://github.com/ant-design/ant-design/issues/477),如果你的公司和产品从中受益,欢迎留言。 @@ -17,31 +16,31 @@ Ant Design 目前在外部也有 [许多产品实践](https://github.com/ant-des ### 金融云 - + 金融云是面向金融机构深度定制的行业云计算服务;助力金融机构向新金融转型升级,推动平台、数据和技术方面的能力全面对外开放。 -立即访问 +

    立即访问

    --- ### OceanBase Cloud Platform - + OceanBase 是一款真正意义上的云端分布式关系型数据库,而 OceanBase Cloud Platform(云平台)是以 OceanBase 数据库为基础的云服务,可以帮助用户快速创建、使用 OB 服务。 -立即访问 +

    立即访问

    --- ### 服务宝体验平台 - + @@ -49,19 +48,19 @@ OceanBase 是一款真正意义上的云端分布式关系型数据库,而 Oce 体验平台是收集用户与公司所有的接触点(包括来电咨询/微博等渠道)的数据,通过数据挖掘和体验同学运营的方式推送给公司内部的业务团队/产品经理,并推动体验问题解决,从而实现良性运转流。 -立即访问 +

    立即访问

    --- ### AntV - + AntV 将数据图形小组近几年在探索数据可视化过程中取得的成果进行总结和沉淀,并分享给所有需要数据可视理论的人。 -立即访问 +

    立即访问

    - - diff --git a/docs/react/getting-started.en-US.md b/docs/react/getting-started.en-US.md new file mode 100644 index 0000000000..8802d404d2 --- /dev/null +++ b/docs/react/getting-started.en-US.md @@ -0,0 +1,176 @@ +--- +order: 1 +title: Getting Started +--- + +Ant Design React is dedicated to providing a **good development experience** for programmers. + +--- + +Before delving into Ant Design React, a good knowledge of [React](http://facebook.github.io/react/) and [JavaScript ES2015](http://babeljs.io/docs/learn-es2015/) is needed. + +## First Example + +The following CodePen demo is the simplest usage case, and it's also a good habit to fork this demo to provide a re-producible demo while reporting a bug. Please don't use this demo as a scaffold in real world. + +- [antd CodePen](http://codepen.io/anon/pen/wGOWGW?editors=001) + +## Standard Development Flow + +During development, you may need to compile and debug JSX and ES2015 code, and even proxy some of the request to mocked data or some external services. And all of these to be done with a quick feedback provided through hot reloading of changes. + +Such features, together with packaging the production version are covered in this work flow. + +### 1. Installation + +> Please make sure that you had installed [Node.js](https://nodejs.org/en/)(> v4.x) before using `antd-init`. + +```bash +$ npm install antd-init -g +``` + +Read [the documentation of `antd-init`](https://github.com/ant-design/antd-init/) and [the documentation of `ant-tool`](http://ant-tool.github.io/) to explore more features. + +> Also, you can use scaffold/demo which is provided by community: +> +> - [react-redux-antd](https://github.com/okoala/react-redux-antd) +> - [react-antd-admin](https://github.com/fireyy/react-antd-admin) +> - [react-antd-redux-router-starter](https://github.com/yuzhouisme/react-antd-redux-router-starter) +> - [react-redux-antd-starter](https://github.com/BetaRabbit/react-redux-antd-starter) +> - [more](https://github.com/ant-design/ant-design/issues/129) + +### 2. Create a New Project + +A new project can be created using CLI tools. + +```bash +$ mkdir antd-demo && cd antd-demo +$ antd-init --type plain-react +``` + +`antd-init` will run `npm install` after a project is created. If it fails, you can run `npm install` by yourself. + +### 3. Use antd's Components + +By default, besides the scaffolding needed to start the development, a fully working Todo application is created. +You may study this example later. For now, just follow this guide in order to get some experience working with the result of `antd-init`. + +Replace the content of `src/entries/index.js` with the following code. +As you can see, there is no difference between antd's components and usual React components. + +```jsx +import React from 'react'; +import ReactDOM from 'react-dom'; +import { DatePicker, message } from 'antd'; + +class App extends React.Component { + constructor(props) { + super(props); + this.state = { + date: '', + }; + } + handleChange(date) { + message.info('Selected Date: ' + date.toString()); + this.setState({ date }); + } + render() { + return ( +
    + this.handleChange(value)} /> +
    Date: {this.state.date.toString()}
    +
    + ); + } +} + +ReactDOM.render(, document.getElementById('root')); +``` + +> All the components in antd are listed in the sidebar. + +### 4. Development & Debugging + +Run your project and visit http://127.0.0.1:8989 + +```bash +$ npm start +``` + +### 5. Building & Deployment + +```bash +$ npm run build +``` + +Entry files will be built and generated in `dist` directory, then we can deploy it to different environments. + +> This guide is designed to help you to understand how to use antd, so it may not be similar to what you do in the real world. +> But you can use those tools in your project, depending on your context and needs. + +## Compatibility + +Ant Design React supports all the modern browsers and IE8+. + +But we need to provide [es5-shim](https://facebook.github.io/react/docs/working-with-the-browser.html#browser-support) and other polyfills for IE8/9, and [babel-polyfill](https://babeljs.io/docs/usage/polyfill/) is a better choice. What's more, use [react@0.14.x](https://facebook.github.io/react/blog/2016/01/12/discontinuing-ie8-support.html) to support IE8. + +```html + + + + + + + + + + + + + + + + +``` + +You may encounter problems like [#28](https://github.com/ant-tool/atool-build/issues/28) and [#858](https://github.com/ant-design/ant-design/issues/858), since `babel@6.x` doesn't support IE8. + +[antd-init](http://github.com/ant-design/antd-init) had solved those problems and you can refer to this [webpack config](https://github.com/ant-design/antd-init/blob/f5fb9479ca973fade51fd6754e50f8b3fafbb1df/boilerplate/webpack.config.js#L4-L8). + +> More about how to use React in IE8: https://github.com/xcatliu/react-ie8 + +## Customized Work Flow + +If you want to customize your work flow, we recommend to use [webpack](http://webpack.github.io/) to build and debug code. + +Also, you can use any [scaffold](https://github.com/enaqx/awesome-react#boilerplates) available in React ecosystem. If you encounter problems, you can use our [webpack config](https://github.com/ant-tool/atool-build/blob/master/src/getWebpackCommonConfig.js) and [modify it](http://ant-tool.github.io/webpack-config.html). + +There are some [scaffolds](https://github.com/ant-design/ant-design/issues/129) which have already integrated antd, so you can try and start with one of these, and even contribute. + +## Import on Demand + +If we import a component like this `import { Button } from 'antd';`, then all the components of antd will be imported. But, we can import component on demand: + +```jsx +import Button from 'antd/lib/button'; +``` + +If you use `babel`, we recommend to use [babel-plugin-antd](https://github.com/ant-design/babel-plugin-antd). This plugin will convert the following code to the above form: + +```jsx +import { Button } from 'antd'; +``` + +And this plugin can also load styles on demand. See the [usage](https://github.com/ant-design/babel-plugin-antd#usage) for further details. + +## Customization + +- [Customize Theme](https://github.com/ant-design/antd-init/tree/master/examples/customize-antd-theme) +- [Local Iconfont](https://github.com/ant-design/antd-init/tree/master/examples/local-iconfont) + +## Tips + +- You can use any `npm` modules. +- We recommend to write code in [ES2015](http://babeljs.io/blog/2015/06/07/react-on-es6-plus) as `babel` has been integrated in our work flow. diff --git a/docs/react/getting-started.md b/docs/react/getting-started.md deleted file mode 100644 index b9ca914184..0000000000 --- a/docs/react/getting-started.md +++ /dev/null @@ -1,150 +0,0 @@ -# 快速上手 - -- category: 1 -- order: 1 - ---- - -Ant Design React 致力于提供给程序员**愉悦**的开发体验。 - -## 第一个例子 - -最简单的试用方式参照以下 CodePen 演示,也推荐 Fork 本例来进行 `Bug Report`,注意不要在实际项目中这样使用。 - -- [antd CodePen](http://codepen.io/anon/pen/pgdXYp?editors=001) - - -## 标准开发 - -实际项目开发中,你会需要对 ES2015 和 JSX 代码的构建、调试、代理、打包部署等一系列工程化的需求。 -我们提供了一套 `npm` + `webpack` 的开发工具链来辅助开发,下面我们用一个简单的实例来说明。 - -### 1. 安装脚手架工具 - -> 使用 `antd-init` 前,务必确认 [Node.js](https://nodejs.org/en/) 已经升级到 v4.x 或以上。 - -```bash -$ npm install antd-init -g -``` - -更多功能请参考 [脚手架工具](https://github.com/ant-design/antd-init/) 和 [开发工具文档](http://ant-tool.github.io/)。 - -### 2. 创建一个项目 - -使用命令行进行初始化。 - -```bash -$ mkdir antd-demo && cd antd-demo -$ antd-init -$ npm install -``` - -若安装缓慢报错,可尝试用 `cnpm` 或别的镜像源自行安装:`rm -rf node_modules && cnpm install`. - -### 3. 使用组件 - -编辑 `src/component/App.jsx`,用 React 的方式直接使用 Ant Design React 的组件。 - -```jsx -import React from 'react'; -import { DatePicker, message } from 'antd'; - -const App = React.createClass({ - getInitialState() { - return { - date: '' - }; - }, - handleChange(value) { - message.info('您选择的日期是: ' + value.toString()); - this.setState({ - date: value - }); - }, - render() { - return
    - -
    当前日期:{this.state.date.toString()}
    -
    ; - } -}); - -export default App; -``` - -你可以在 [这里](/components/button) 选用更多组件。 - -### 4. 开发调试 - -一键启动调试,访问 http://127.0.0.1:8989 查看效果。 - -```bash -$ npm run dev -``` - -### 5. 构建和部署 - -```bash -$ npm run build -``` - -入口文件会构建到 `dist` 目录中,你可以自由部署到不同环境中进行引用。 - -> 上述例子用于帮助你理解 Ant Design React 的使用流程,并非真实的开发过程,你可以根据自己的项目开发流程进行接入。 - -## 兼容性 - -Ant Design React 支持所有的现代浏览器和 IE8+。 - -对于 IE8,需要提供 [es5-shim](http://facebook.github.io/react/docs/working-with-the-browser.html#browser-support-and-polyfills) 等 Polyfills 的支持。 - -
    - - - -```html - - - - - - - - - - - - - - - - -``` - -另外,由于 babel 对 IE8 的支持不佳,你可能会遇到类似 [#28](https://github.com/ant-tool/atool-build/issues/28) 和 [#858](https://github.com/ant-design/ant-design/issues/858) 的 default 报错的问题。 - -[antd-init](http://github.com/ant-design/antd-init) 脚手架已经解决了这个问题,你也可以参照这个 [webpack 配置](https://github.com/ant-design/antd-init/blob/8c4a55d205c82a6ad87814bbf997696051713d58/boilerplate/webpack.config.js#L10-L14)。 - -> 更多 IE8 下使用 React 的相关问题可以参考:https://github.com/xcatliu/react-ie8 - -## 自行构建 - -如果想自己维护工作流,我们推荐使用 [webpack](http://webpack.github.io/) 进行构建和调试。理论上你可以利用 React 生态圈中的 [各种脚手架](https://github.com/enaqx/awesome-react#boilerplates) 进行开发,如果遇到问题可参考我们所使用的 [webpack 配置](https://github.com/ant-tool/atool-build/blob/master/src/getWebpackCommonConfig.js) 进行 [定制](http://ant-tool.github.io/webpack-config.htm)。 - -### 改变主色系 - -- [配置代码示例](https://github.com/ant-design/antd-init/tree/master/examples/customize-antd-theme) - -## 小甜点 - -- 你可以享用 `npm` 生态圈里的所有模块。 -- 我们使用了 `babel`,试试用 [ES6](http://babeljs.io/blog/2015/06/07/react-on-es6-plus/) 的写法来提升编码的愉悦感。 diff --git a/docs/react/getting-started.zh-CN.md b/docs/react/getting-started.zh-CN.md new file mode 100644 index 0000000000..90ca2891a3 --- /dev/null +++ b/docs/react/getting-started.zh-CN.md @@ -0,0 +1,172 @@ +--- +order: 1 +title: 快速上手 +--- + +Ant Design React 致力于提供给程序员**愉悦**的开发体验。 + +--- + +在开始之前,推荐先学习 [React](http://facebook.github.io/react/) 和 [ES2015](http://babeljs.io/docs/learn-es2015/)。 + +## 第一个例子 + +最简单的使用方式参照以下 CodePen 演示,也推荐 Fork 本例来进行 `Bug Report`,注意不要在实际项目中这样使用。 + +- [antd CodePen](http://codepen.io/anon/pen/wGOWGW?editors=001) + +## 标准开发 + +实际项目开发中,你会需要对 ES2015 和 JSX 代码的构建、调试、代理、打包部署等一系列工程化的需求。 +我们提供了一套 `npm` + `webpack` 的开发工具链来辅助开发,下面我们用一个简单的实例来说明。 + +### 1. 安装脚手架工具 + +> 使用 `antd-init` 前,务必确认 [Node.js](https://nodejs.org/en/) 已经升级到 v4.x 或以上。 + +```bash +$ npm install antd-init -g +``` + +更多功能请参考 [脚手架工具](https://github.com/ant-design/antd-init/) 和 [开发工具文档](http://ant-tool.github.io/)。 + +> 除了官方提供的脚手架,您也可以使用社区提供的脚手架和范例: +> +> - [react-redux-antd](https://github.com/okoala/react-redux-antd) +> - [react-antd-admin](https://github.com/fireyy/react-antd-admin) +> - [react-antd-redux-router-starter](https://github.com/yuzhouisme/react-antd-redux-router-starter) +> - [react-redux-antd-starter](https://github.com/BetaRabbit/react-redux-antd-starter) +> - [更多](https://github.com/ant-design/ant-design/issues/129) + +### 2. 创建一个项目 + +使用命令行进行初始化。 + +```bash +$ mkdir antd-demo && cd antd-demo +$ antd-init --type plain-react +``` + +antd-init 会自动安装 npm 依赖,若有问题则可自行安装。 + +若安装缓慢报错,可尝试用 `cnpm` 或别的镜像源自行安装:`rm -rf node_modules && cnpm install`。 + +### 3. 使用组件 + +脚手架会生成一个 Todo 应用实例(一个很有参考价值的 React 上手示例),先不管它,我们用来测试组件。 + +直接用下面的代码替换 `src/entries/index.js` 的内容,用 React 的方式直接使用 antd 组件。 + +```jsx +import React from 'react'; +import ReactDOM from 'react-dom'; +import { DatePicker, message } from 'antd'; + +class App extends React.Component { + constructor(props) { + super(props); + this.state = { + date: '', + }; + } + handleChange(date) { + message.info('您选择的日期是: ' + date.toString()); + this.setState({ date }); + } + render() { + return ( +
    + this.handleChange(value)} /> +
    当前日期:{this.state.date.toString()}
    +
    + ); + } +} + +ReactDOM.render(, document.getElementById('root')); +``` + +> 你可以在左侧菜单选用更多组件。 + +### 4. 开发调试 + +一键启动调试,访问 http://127.0.0.1:8989 查看效果。 + +```bash +$ npm start +``` + +### 5. 构建和部署 + +```bash +$ npm run build +``` + +入口文件会构建到 `dist` 目录中,你可以自由部署到不同环境中进行引用。 + +> 上述例子用于帮助你理解 Ant Design React 的使用流程,并非真实的开发过程,你可以根据自己的项目开发流程进行接入。 + +## 兼容性 + +Ant Design React 支持所有的现代浏览器和 IE8+。 + +对于 IE8/9 等浏览器,需要提供 [es5-shim](https://facebook.github.io/react/docs/working-with-the-browser.html#browser-support) 等 Polyfills 的支持,推荐使用 [babel-polyfill](https://babeljs.io/docs/usage/polyfill/)。特别对于 IE8 需要配合使用 [react@0.14.x](https://facebook.github.io/react/blog/2016/01/12/discontinuing-ie8-support.html) 版本。 + +```html + + + + + + + + + + + + + + + + +``` + +另外,由于 `babel@6.x` 对 IE8 的支持不佳,你可能会遇到类似 [#28](https://github.com/ant-tool/atool-build/issues/28) 和 [#858](https://github.com/ant-design/ant-design/issues/858) 的 default 报错的问题。 + +[antd-init](http://github.com/ant-design/antd-init) 脚手架已经解决了这个问题,你也可以参照这个 [webpack 配置](https://github.com/ant-design/antd-init/blob/f5fb9479ca973fade51fd6754e50f8b3fafbb1df/boilerplate/webpack.config.js#L4-L8)。 + +> 更多 IE8 下使用 React 的相关问题可以参考:https://github.com/xcatliu/react-ie8 + +## 自行构建 + +如果想自己维护工作流,我们推荐使用 [webpack](http://webpack.github.io/) 进行构建和调试。理论上你可以利用 React 生态圈中的 [各种脚手架](https://github.com/enaqx/awesome-react#boilerplates) 进行开发,如果遇到问题可参考我们所使用的 [webpack 配置](https://github.com/ant-tool/atool-build/blob/master/src/getWebpackCommonConfig.js) 进行 [定制](http://ant-tool.github.io/webpack-config.html)。 + +目前社区也有很多基于 antd 定制的 [脚手架](https://github.com/ant-design/ant-design/issues/129),欢迎进行试用和贡献。 + +## 按需加载 + +通过 `import { Button } from 'antd';` 引入会加载 antd 下所有的模块,如果要按需加载可以通过以下的写法来引用。 + +```jsx +import Button from 'antd/lib/button'; +``` + +如果你使用 babel,我们推荐使用 [babel-plugin-antd](https://github.com/ant-design/babel-plugin-antd) 来进行按需加载,加入这个插件后。你可以仍然这么写: + +```jsx +import { Button } from 'antd'; +``` + +插件会帮你转换成上面的写法。另外此插件配合 [style](https://github.com/ant-design/babel-plugin-antd#usage) 属性可以做到模块样式的按需自动加载。 + +## 配置案例 + +- [改变主色系](https://github.com/ant-design/antd-init/tree/master/examples/customize-antd-theme) +- [使用本地字体](https://github.com/ant-design/antd-init/tree/master/examples/local-iconfont) + +## 小甜点 + +- 你可以享用 `npm` 生态圈里的所有模块。 +- 我们使用了 `babel`,试试用 [ES2015](http://babeljs.io/blog/2015/06/07/react-on-es6-plus) 的写法来提升编码的愉悦感。 diff --git a/docs/react/install.en-US.md b/docs/react/install.en-US.md new file mode 100644 index 0000000000..6be2e4d820 --- /dev/null +++ b/docs/react/install.en-US.md @@ -0,0 +1,68 @@ +--- +order: 2 +title: Installation +--- + +## Using npm to install + +**We recommend using npm to install**,it not only makes development easier,but you can also take advantage of the whole ecosystem. + + +If using npm to install, you could use `import` or `require`. + +Stable version: + +[![npm package](http://img.shields.io/npm/v/antd.svg?style=flat-square)](https://www.npmjs.org/package/antd) + +```bash +$ npm install antd --save +``` + +You can Subscribe to this feed for new version notification: https://github.com/ant-design/ant-design/releases.atom + + +Beta version: + +[![](https://cnpmjs.org/badge/v/antd.svg?&tag=beta&subject=npm)](https://www.npmjs.org/package/antd) + +```bash +$ npm install antd@beta --save +``` + +> **Past releases**:https://github.com/ant-design/ant-design/releases + +## Import in Browser + +We provide `antd.js` `antd.css` and `antd.min.js` `antd.min.css` under `antd/dist` in antd's npm package, in order to import all the components of antd directly. Also, you can use [npmcdn](https://npmcdn.com/). + +> It's not recommended to use the already built files, as you cannot get bugfixes from the dependencies of antd. + +#### stable + +- https://npmcdn.com/antd/dist/antd.js +- https://npmcdn.com/antd/dist/antd.css +- https://npmcdn.com/antd/dist/antd.min.js +- https://npmcdn.com/antd/dist/antd.min.css + +#### beta + +- https://npmcdn.com/antd@beta/dist/antd.js +- https://npmcdn.com/antd@beta/dist/antd.css +- https://npmcdn.com/antd@beta/dist/antd.min.js +- https://npmcdn.com/antd@beta/dist/antd.min.css + +> Here is an [example](https://github.com/ant-design/antd-init/tree/master/examples/build-antd-standalone) about how to build your own antd.js if you are using antd@<1.0.0. + +## Development tool + +We provide React components [Scaffold tool](https://github.com/ant-design/antd-init). + +```bash +$ npm install antd-init -g +``` + +Inside an empty folder run `antd-init` to init. + +You can explore the latest structure of scaffold [there]((https://github.com/ant-design/antd-init/tree/master/boilerplates)), it is a good habit to watch this repo to get the latest features. + +> [More development tools](http://ant-tool.github.io/)。 diff --git a/docs/react/install.md b/docs/react/install.md deleted file mode 100644 index f72ac9e492..0000000000 --- a/docs/react/install.md +++ /dev/null @@ -1,43 +0,0 @@ -# 安装 - -- category: 2 -- order: 2 - ---- - -## 使用 npm 安装 - -**我们推荐使用 npm 的方式进行开发**,不仅可在开发环境轻松调试,也可放心地在生产环境打包部署使用,享受整个生态圈和工具链带来的诸多好处。 - -可以通过 npm 直接安装到项目中,使用 `import` 或 `require` 进行引用。 - -稳定版: - -[![npm package](http://img.shields.io/npm/v/antd.svg?style=flat-square)](https://www.npmjs.org/package/antd) - -```bash -$ npm install antd --save -``` - -开发版本: - -[![](https://cnpmjs.org/badge/v/antd.svg?&tag=beta&subject=npm)](https://www.npmjs.org/package/antd) - -```bash -$ npm install antd@beta --save -``` - -> **历史版本**:https://github.com/ant-design/ant-design/releases - - -## 开发工具 - -我们提供了 React 前端应用开发的 [脚手架工具](https://github.com/ant-design/antd-init),可以安装到全局直接使用。 - -```bash -$ npm install antd-init -g -``` - -在空目录运行 `antd-init` 可以初始化一个 antd 的前端应用。 - -> 更多开发工具 [使用方式](http://ant-tool.github.io/)。 diff --git a/docs/react/install.zh-CN.md b/docs/react/install.zh-CN.md new file mode 100644 index 0000000000..0ecc24744a --- /dev/null +++ b/docs/react/install.zh-CN.md @@ -0,0 +1,67 @@ +--- +order: 2 +title: 安装 +--- + +## 使用 npm 安装 + +**我们推荐使用 npm 的方式进行开发**,不仅可在开发环境轻松调试,也可放心地在生产环境打包部署使用,享受整个生态圈和工具链带来的诸多好处。 + +可以通过 npm 直接安装到项目中,使用 `import` 或 `require` 进行引用。 + +稳定版: + +[![npm package](http://img.shields.io/npm/v/antd.svg?style=flat-square)](https://www.npmjs.org/package/antd) + +```bash +$ npm install antd --save +``` + +你可以订阅:https://github.com/ant-design/ant-design/releases.atom 来获得稳定版发布的通知。 + +开发版本: + +[![](https://cnpmjs.org/badge/v/antd.svg?&tag=beta&subject=npm)](https://www.npmjs.org/package/antd) + +```bash +$ npm install antd@beta --save +``` + +> **历史版本**:https://github.com/ant-design/ant-design/releases + + +## 浏览器引入 + +我们在 npm 发布包内的 `antd/dist` 目录下提供了 `antd.js` `antd.css` 以及 `antd.min.js` `antd.min.css` 用于一次性引入所有的 antd 组件,也可以通过 [npmcdn](https://npmcdn.com/) 进行下载。 + +> 不推荐使用构建文件,因为难以获得底层依赖模块的 bug 快速修复支持。 + +#### stable + +- https://npmcdn.com/antd/dist/antd.js +- https://npmcdn.com/antd/dist/antd.css +- https://npmcdn.com/antd/dist/antd.min.js +- https://npmcdn.com/antd/dist/antd.min.css + +#### beta + +- https://npmcdn.com/antd@beta/dist/antd.js +- https://npmcdn.com/antd@beta/dist/antd.css +- https://npmcdn.com/antd@beta/dist/antd.min.js +- https://npmcdn.com/antd@beta/dist/antd.min.css + +> 对于 1.0 之前的版本,这里有一个 [自行构建的例子](https://github.com/ant-design/antd-init/tree/master/examples/build-antd-standalone) 以供参考。 + +## 开发工具 + +我们提供了 React 前端应用开发的 [脚手架工具](https://github.com/ant-design/antd-init),可以安装到全局直接使用。 + +```bash +$ npm install antd-init -g +``` + +在空目录运行 `antd-init` 可以初始化一个 antd 的前端应用。 + +最新的脚手架结构可以到这里 [查看](https://github.com/ant-design/antd-init/tree/master/boilerplates),建议持续关注更新以便获得最新的开发工程特性。 + +> 更多开发工具 [使用方式](http://ant-tool.github.io/)。 diff --git a/docs/react/install_en.md b/docs/react/install_en.md deleted file mode 100644 index 7dc0c54ec4..0000000000 --- a/docs/react/install_en.md +++ /dev/null @@ -1,37 +0,0 @@ -## Using npm to install - -**We recommend use npm to install**,Not only it makes development easier,also you can take advantage of the whole ecosystem. - - -If using npm to install, you could use `import` or `require`. - -Stable version: - -[![npm package](http://img.shields.io/npm/v/antd.svg?style=flat-square)](https://www.npmjs.org/package/antd) - -```bash -$ npm install antd --save -``` - -Beta version: - -[![](https://cnpmjs.org/badge/v/antd.svg?&tag=beta&subject=npm)](https://www.npmjs.org/package/antd) - -```bash -$ npm install antd@beta --save -``` - -> **Past releases**:https://github.com/ant-design/ant-design/releases - - -## Development tool - -We provide React components [Scaffold tool](https://github.com/ant-design/antd-init). - -```bash -$ npm install antd-init -g -``` - -Inside an empty folder run `antd-init` to init. - -> [More development tools](http://ant-tool.github.io/)。 diff --git a/docs/react/introduce.en-US.md b/docs/react/introduce.en-US.md new file mode 100644 index 0000000000..24a79e9832 --- /dev/null +++ b/docs/react/introduce.en-US.md @@ -0,0 +1,100 @@ +--- +order: 0 +title: Ant Design of React +--- + +antd is a set of React components which follow the Ant Design specification. It is designed to help developing RIA such as dashboards or other enterprise-like complex UI needs. + +
    + + + + +
    + + + +--- + +## Features + +- Using Ant Design, a design language for creating user friendly and beautiful websites. +- It is a set of high quality UI components and based on [React Component](http://react-component.github.io/badgeboard/). +- Provides a work flow which is based on npm, webpack, and babel, supporting ES2015 and TypeScript. + +## Installation + +```bash +$ npm install antd +``` + +## Example + +```jsx +import { DatePicker } from 'antd'; +ReactDOM.render(, mountNode); +``` + +Import style: + +```jsx +import 'antd/dist/antd.css'; // or 'antd/dist/antd.less' +``` + +You can use: + +- `import DatePicker from 'antd/lib/date-picker';` +- `import { DatePicker } from 'antd';` when [babel-plugin-antd](https://github.com/ant-design/babel-plugin-antd) is also used. + +> babel-plugin-antd supports importing components and styles on demand. + +## Version + +- Stable: [![npm package](http://img.shields.io/npm/v/antd.svg?style=flat-square)](https://www.npmjs.org/package/antd) +- Beta: [![](https://cnpmjs.org/badge/v/antd.svg?&tag=beta&subject=npm)](https://www.npmjs.org/package/antd) + +## Compatibility + +Modern browsers and IE8+. + +> [IE8 issues](https://github.com/xcatliu/react-ie8) + +## Useful Links + +- [Home Page](http://ant.design/) +- [Change Log](/changelog) +- [Scaffold](https://github.com/ant-design/antd-init/) +- [Development Tools](http://ant-tool.github.io/) +- [React Component](http://react-component.github.io/) +- [Ant Design Mobile](http://mobile.ant.design) +- [React Code Style](https://github.com/react-component/react-component.github.io/blob/master/docs/zh-cn/component-code-style.md) +- [Component Design Principles](https://github.com/react-component/react-component.github.io/blob/master/docs/zh-cn/component-design.md) +- [Design Handbook](https://os.alipayobjects.com/rmsportal/HTXUgPGkyyxEivE.png) +- [Scaffold and Demo Supported by Community](https://github.com/ant-design/ant-design/issues/129) + +## Who is using antd + +- Ant Financial +- Alibaba +- Koubei +- China Internet Plus +- Didi + +> If your company or product uses Ant Design, you are welcome to comment in [this issue]((https://github.com/ant-design/ant-design/issues/477)). Thank you! + +## Contributing + +Please read our [CONTRIBUTING.md](https://github.com/ant-design/ant-design/blob/master/.github/CONTRIBUTING.md) first. + +If you have any idea to improve antd, just create a [Pull Request](https://github.com/ant-design/ant-design/pulls). Also, you can also [issue](https://github.com/ant-design/ant-design/issues/new) bugs or [ask questions](https://github.com/ant-design/ant-design/issues). + +> Recommend to read [*How To Ask Questions The Smart Way*](http://www.catb.org/~esr/faqs/smart-questions.html) and [How to Ask a Question in Open Source Community](https://github.com/seajs/seajs/issues/545) and [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html), a smart question will get right answer quickly. diff --git a/docs/react/introduce.md b/docs/react/introduce.zh-CN.md similarity index 59% rename from docs/react/introduce.md rename to docs/react/introduce.zh-CN.md index f0cdb0b2d1..5758796793 100644 --- a/docs/react/introduce.md +++ b/docs/react/introduce.zh-CN.md @@ -1,8 +1,6 @@ -# Ant Design of React - -- category: 0 -- order: 0 - +--- +order: 0 +title: Ant Design of React --- 这里是 Ant Design 的 React 实现,开发和服务于企业级后台产品。 @@ -12,6 +10,7 @@ +
    + +--- ## 特性 - Designed as Ant Design,提炼和服务企业级中后台产品的交互语言和视觉风格。 -- [React Component](http://react-component.github.io/badgeboard/) 上精心封装的高质量 UI 库。 -- 基于 npm + webpack + babel 的工作流,支持 ES2015。 +- [React Component](http://react-component.github.io/badgeboard/) 基础上精心封装的高质量 UI 组件。 +- 基于 npm + webpack + babel 的工作流,支持 ES2015 和 TypeScript。 ## 安装 @@ -47,11 +47,15 @@ ReactDOM.render(, mountNode); 引入样式: ```jsx -import 'antd/lib/index.css'; // or 'antd/style/index.less' +import 'antd/dist/antd.css'; // or 'antd/dist/antd.less' ``` -按需加载可通过此写法 `import DatePicker from 'antd/lib/date-picker'` 或使用插件 [babel-plugin-antd](https://github.com/ant-design/babel-plugin-antd)。 +以下两种方法都可以达到按需加载的目的: +- `import DatePicker from 'antd/lib/date-picker'` +- 配合插件 [babel-plugin-antd](https://github.com/ant-design/babel-plugin-antd) 使用 `import { DatePicker } from 'antd';` + +> babel-plugin-antd 支持 js 和 css 同时按需加载。 ## 版本 @@ -67,21 +71,31 @@ import 'antd/lib/index.css'; // or 'antd/style/index.less' ## 链接 - [首页](http://ant.design/) -- [修改记录](http://ant.design/changelog) +- [更新日志](/changelog) - [开发脚手架](https://github.com/ant-design/antd-init/) - [开发工具文档](http://ant-tool.github.io/) -- [React 组件](http://react-component.github.io/) +- [React 基础组件](http://react-component.github.io/) +- [移动端组件](http://mobile.ant.design) - [React 代码规范](https://github.com/react-component/react-component.github.io/blob/master/docs/zh-cn/component-code-style.md) - [组件设计原则](https://github.com/react-component/react-component.github.io/blob/master/docs/zh-cn/component-design.md) - [设计规范速查手册](https://os.alipayobjects.com/rmsportal/HTXUgPGkyyxEivE.png) +- [社区贡献脚手架和范例](https://github.com/ant-design/ant-design/issues/129) +- [常见问题](https://github.com/ant-design/ant-design/wiki/FAQ) +- [CodePen 模板](http://codepen.io/anon/pen/wGOWGW?editors=001) +- [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design) ## 谁在使用 - 蚂蚁金服 +- 阿里巴巴 - 口碑 +- 新美大 +- 滴滴 > 如果你的公司和产品使用了 Ant Design,欢迎到 [这里](https://github.com/ant-design/ant-design/issues/477) 留言。 ## 如何贡献 -在任何形式的参与前,请先阅读 [贡献者文档](https://github.com/ant-design/ant-design/blob/master/.github/CONTRIBUTING.md)。有任何建议或意见您可以 [Pull Request](https://github.com/ant-design/ant-design/pulls),给我们 [报告 Bug](http://dwz.cn/2AF9ao) 或 [提问](https://github.com/ant-design/ant-design/issues)。 +在任何形式的参与前,请先阅读 [贡献者文档](https://github.com/ant-design/ant-design/blob/master/.github/CONTRIBUTING.md)。有任何建议或意见您可以 [Pull Request](https://github.com/ant-design/ant-design/pulls),给我们 [报告 Bug](https://github.com/ant-design/ant-design/issues/new) 或 [提问](https://github.com/ant-design/ant-design/issues)。 + +> 强烈推荐阅读 [《提问的智慧》](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),更好的问题更容易获得帮助。 diff --git a/docs/react/upgrade-notes.md b/docs/react/upgrade-notes.md index 71acaf02f8..edc2428054 100644 --- a/docs/react/upgrade-notes.md +++ b/docs/react/upgrade-notes.md @@ -1,9 +1,13 @@ -# 升级指南 - -- category: 3 -- order: 3 - --- +order: 4 +english: 升级指南 +--- + +此处着重列出升级中的不兼容变化和推荐改动。所有变动请见 [更新日志](/changelog)。 + +## 0.12 => 1.0 + +`1.0.0` 之后将不再单独提供升级指南,请参考对应版本 [更新日志](/changelog#1.0.0) 中的 `不兼容改动` 部分进行升级。 ## 0.11 => 0.12 @@ -11,7 +15,7 @@ ### 使用 Form 提供的校验功能代替 Validation -Validation 已经被废弃,并会在以后的版本完全移除,所以建议尽快使用 Form 自带的校验功能替换 Validation。具体使用方式可以查阅文档和例子([#1](http://ant.design/components/form/#demo-validate-basic) [#2](http://ant.design/components/form/#demo-validate-other) [#3](http://ant.design/components/form/#demo-validate-customized))。 +Validation 已经被废弃,并会在以后的版本完全移除,所以建议尽快使用 Form 自带的校验功能替换 Validation。具体使用方式可以查阅文档和例子([#1](http://ant.design/components/form/#demo-validate-basic) [#2](http://ant.design/components/form/#demo-validate-other) [#3](http://ant.design/components/form/#demo-validate-customized))。 ### Progress `format` 属性的值改为函数 @@ -75,6 +79,10 @@ import 'antd/style/index.less'; 如果无法理解受控组件,只须在发现在选中日期后 `Datepicker` 显示的值不变的情况后,把其 `value` 属性改为 `defaultValue` 即可。 +### TimePicker locale 结构改变 + +属性 `locale` 结构发生了 [变化](https://github.com/ant-design/ant-design/commit/fd1312803fd49586ded9af39d923457540c515cc#diff-fe4bfc98d91fc3dab8f391e3258622d4L1),需要将原有的属性改为现有的[结构](https://github.com/ant-design/ant-design/issues/1270#issuecomment-201181384)。 + ### 其他 - Alert 组件默认不展示样式,可以用 `showIcon` 属性添加图标。 @@ -138,4 +146,4 @@ import 'antd/style/index.less'; - Slider 的 `withDots` `isIncluded` 属性修改为 `dots` `included`。 - iconfont 的基线更新,可能导致原有图标的位置偏移。 -新版本变化较大,以上升级指南可能有遗漏,全部改动可以参考 [Changelog](/changelog)。在升级过程中遇到问题,欢迎 [报告](https://github.com/ant-design/ant-design/issues/new) 给我们。 +新版本变化较大,以上升级指南可能有遗漏,全部改动可以参考 [更新日志](/changelog)。在升级过程中遇到问题,欢迎 [报告](https://github.com/ant-design/ant-design/issues/new) 给我们。 diff --git a/docs/resource/download.en-US.md b/docs/resource/download.en-US.md new file mode 100644 index 0000000000..89b6521738 --- /dev/null +++ b/docs/resource/download.en-US.md @@ -0,0 +1,7 @@ +--- +order: 0 +chinese: 资源下载 +english: Download +--- + +TBD diff --git a/docs/resource/download.md b/docs/resource/download.zh-CN.md similarity index 81% rename from docs/resource/download.md rename to docs/resource/download.zh-CN.md index 1d17a2f27d..3dbb6fb436 100644 --- a/docs/resource/download.md +++ b/docs/resource/download.zh-CN.md @@ -1,24 +1,23 @@ -# 资源下载 - -- order: 0 -- category: 0 - +--- +order: 0 +chinese: 资源下载 +english: Download --- 这里提供 Ant Design 相关设计资源和设计工具的下载,更多设计资源正在整理和完善中。
    - + - Axure Components + Axure Components v2.0.1 一套强大的 Ant Design 的 Axure 部件库 - + - Axure Box + Axure Box v1.3 强大的 Ant Design 组件拼装方式 @@ -29,7 +28,7 @@ 一套页面逻辑原型库,帮你梳理页面逻辑 - + Web Font diff --git a/docs/resource/github.md b/docs/resource/github.md index de1fb5c623..72ccbf651e 100644 --- a/docs/resource/github.md +++ b/docs/resource/github.md @@ -1,7 +1,6 @@ -# GitHub - -- order: 3 -- category: 3 -- link: https://github.com/ant-design/ant-design - --- +order: 2 +link: //github.com/ant-design/ant-design +english: GitHub +--- + diff --git a/docs/resource/reference.md b/docs/resource/reference.md index 42fdcce6db..67cfe39dc1 100644 --- a/docs/resource/reference.md +++ b/docs/resource/reference.md @@ -1,8 +1,7 @@ -# 文献素材 - -- order: 1 -- category: 1 - +--- +order: 1 +chinese: 文献素材 +english: Reference --- 在进行模式、组件和语言的整理中,《About Face 4》、《Web 界面设计》、《界面设计模式》、《写给大家看的设计书》、《设计心理学》、《Web 表单设计:点石成金的艺术》等书籍给了我们很多的启示,帮助我们节约了大量时间,并成功克服了很多困难。如果想了解更多设计相关的内容,建议你去阅读这些非常棒的书籍。 diff --git a/docs/spec/alignment.en-US.md b/docs/spec/alignment.en-US.md new file mode 100644 index 0000000000..c1bc12f24a --- /dev/null +++ b/docs/spec/alignment.en-US.md @@ -0,0 +1,39 @@ +--- +category: 十大原则 +order: 2 +title: Alignment +--- + +As is described in the Law of Continuity of Gestalt psychology, in the perceptual process, people usually tend to understand the object in the way that it is firstly perceived, to let the straight lines be straight and let the curve lines be curve. In the design of interface, aligning the elements meets users’ perception, also delivers the information to users in a more smooth way. + +> ** Gestalt psychology or gestaltism(German:Gestalttheorie)** :Gestalttheorie is an important genre of psychology. It rose in the beginning of the 20 century in Germany.The central principle of gestalt psychology is that the mind forms a global whole with self-organizing tendencies.『The whole is other than the sum of the parts.』--Quote from Wikipedia + +--- + +## Text Alignment + +good example + + +If the paragraphs or the length of the words are too short or too loose, then a unified visual starting point is needed. + + +--- + +## Form Alignment + +example of colon alignment + +Colon alignment(right-align) can encircle the content into a certain range. Users can infer where the chart is through the regular arranged colon so that the speed of filling in the chart can be speeded up. + + +More ways of aligning,please visit [(/docs/pattern/form#对齐方式)](/docs/pattern/form#对齐方式)。 + +--- + +## Numbers Alignment + +good example +bad example + +To compare the numbers faster, we suggest that all numbers should keep the same digit numbers after decimal point; meanwhile all numbers should be right-aligned. 。 diff --git a/docs/spec/alignment.zh-CN.md b/docs/spec/alignment.zh-CN.md new file mode 100644 index 0000000000..004650f450 --- /dev/null +++ b/docs/spec/alignment.zh-CN.md @@ -0,0 +1,38 @@ +--- +category: 十大原则 +order: 2 +subtitle: Alignment +english: 对齐 +--- + +正如『格式塔学派』中的连续律(Law of Continuity)所描述的,在知觉过程中人们往往倾向于使知觉对象的直线继续成为直线,使曲线继续成为曲线。在界面设计中,将元素进行对齐,既符合用户的认知特性,也能引导视觉流向,让用户更流畅地接收信息。 + +> ** 格式塔学派(德语:Gestalttheorie)** :是心理学重要流派之一,兴起于 20 世纪初的德国,又称为完形心理学;主张人脑的运作原理是整体的,『整体不同于其部件的总和』。——摘自『维基百科』 + +--- + +## 文案类对齐 + +推荐示例 +不推荐示例 + +如果页面的字段或段落较短、较散时,需要确定一个统一的视觉起点。 + +--- + +## 表单类对齐 + +冒号对齐示例 + +冒号对齐(右对齐)能让内容锁定在一定范围内,让用户眼球顺着冒号的视觉流,就能找到所有填写项,从而提高填写效率。 + +更多对齐方式,请查看[『模式/表单/规格/对齐方式』](/docs/pattern/form#对齐方式)。 + +--- + +## 数字类对齐 + +正确示例 +错误示例 + +为了快速对比数值大小,建议所有数值取相同有效位数,并且右对齐。 diff --git a/docs/spec/colors.md b/docs/spec/colors.md index cb093b8d88..56ea8a3307 100644 --- a/docs/spec/colors.md +++ b/docs/spec/colors.md @@ -1,80 +1,22 @@ -# 色彩 - -- category: 基础 -- order: 2 - +--- +category: 设计基础 +order: 2 +chinese: 色彩 +english: Colors --- ## 有意义的色彩 -色彩在界面设计中的使用应同时具备品牌识别性以及界面设计功能性。众所周知色彩是相当感性的东西,设计中对色彩的运用首要应考虑到品牌层面的表达,另外很重要的一点是色彩的运用应达到信息传递,动作指引,交互反馈,或是强化和凸现某一个元素的目的。任何颜色的选取和使用应该是有意义的。众所周知色彩是相当感性的东西,设计中对色彩的运用首要应考虑到品牌层面的表达,另外很重要的一点是色彩的运用应达到信息传递,动作指引,交互反馈,或是强化和凸现某一个元素的目的。任何颜色的选取和使用应该是有意义的。 +色彩在界面设计中的使用应同时具备品牌识别性以及界面设计功能性。色彩是相当感性的东西,设计中对色彩的运用首要应考虑到品牌层面的表达,另外很重要的一点是色彩的运用应达到信息传递,动作指引,交互反馈,或是强化和凸现某一个元素的目的。任何颜色的选取和使用应该是有意义的。 -## ANTD Color +## Ant Design Colors Ant Design 的色板由 9 种基本色彩组成,每种基本色又衍生出九宫格色板,在此基础上还可以通过黑白叠加的方式实现色彩明暗的效果。 -
    - -## 色彩和交互 - -设计元素本身由于交互行为会引发一系列细微的视觉变化,而元素本身的颜色变化有时也能很好的实现这一目的。在进行这类设计的同时,建议采取在颜色上添加黑色或者白色并按照 `n+5%` 的规律递增的方式来实现。以下图为例,当鼠标 hover 某个特定元素,就视为浮起,对应颜色就相应增加白色叠加,相反点击的行为可以理解为按下去,在颜色上就相应的增加黑色的叠加。 - - - - - -## 色彩识别 - -合适的色彩对比为信息传达加分,同时也应放考虑到有颜色识别障碍人群的需求。我们将每种主色衍生出来的颜色进行了打标,在考虑对比颜色的选择时建议两种颜色对应标签数值的差要大于等于 5。 - - - - - - - -`````jsx -let Palette = React.createClass({ +`````__react +const Palette = React.createClass({ render() { - let color = this.props.color; + const color = this.props.color; return
    {color.colors.map(function(color) { @@ -88,9 +30,9 @@ let Palette = React.createClass({
    ; } }); -let ExtendPalettes = React.createClass({ +const ExtendPalettes = React.createClass({ render() { - let colors = [ + const colors = [ { 'title': 'Primary Color', 'description': '尽管同一种颜色传达的含义会因人而异,受到文化和地域的影响。但颜色还是可以提取出一些共通的特性,例如暖色系的红、橙、黄通常用于象征活力,激情,积极;而冷色系的绿、蓝、紫通常给人感觉是安全、稳定、专业。', @@ -128,11 +70,11 @@ let ExtendPalettes = React.createClass({ ] }, { - 'title': 'Blue #00A0E8', + 'title': 'Blue #00A0E9', 'description': '这里的蓝色沿用的是蚂蚁金服的品牌色,深蓝色的运用可以传递出可靠和稳定的情绪,而浅蓝色系则更为友好和清新,同时还代表了科技感与想象力。在很多专业类、管理类的后台系统设计中蓝色系常常会被选择作为设计的主色来使用。', 'colors': [ "#CCE4F6", "#95CCF5", "#6AC2F5", - "#1D80D3", "#00A0E8", "#2DB7F5", + "#1D80D3", "#00A0E9", "#2DB7F5", "#1F5AA3", "#0B366A", "#08172F" ] }, @@ -189,26 +131,38 @@ let ExtendPalettes = React.createClass({
    ; } }); -ReactDOM.render(, document.getElementById('extend-palettes')); +ReactDOM.render(, mountNode); ````` +## 色彩和交互 + +设计元素本身由于交互行为会引发一系列细微的视觉变化,而元素本身的颜色变化有时也能很好的实现这一目的。在进行这类设计的同时,建议采取在颜色上添加黑色或者白色并按照 `n+5%` 的规律递增的方式来实现。以下图为例,当鼠标 hover 某个特定元素,就视为浮起,对应颜色就相应增加白色叠加,相反点击的行为可以理解为按下去,在颜色上就相应的增加黑色的叠加。 + + + + + +## 色彩识别 + +合适的色彩对比为信息传达加分,同时也应放考虑到有颜色识别障碍人群的需求。我们将每种主色衍生出来的颜色进行了打标,在考虑对比颜色的选择时建议两种颜色对应标签数值的差要大于等于 5。 + + + + + ## 色彩换算工具 > 正数为变淡 `tint` ,负数为加深 `shade`。 -
    - -Ant Design 专用色彩换算工具,用于解析类似 `#2db7f5 tint 80%` 的色彩标注。 - -less 或 scss 语言可以直接使用 `tint(#2db7f5, 80%)` 和 `shade(#2db7f5, 80%)` 的语法。 - - -`````jsx -let Button = antd.Button; -let InputNumber = antd.InputNumber; -let Slider = antd.Slider; -let Tooltip = antd.Tooltip; -let TintShadeTool = React.createClass({ +`````__react +const Values = require('values.js'); +const CopyToClipboard = require('react-copy-to-clipboard'); +const antd = require('antd'); +const Button = antd.Button; +const InputNumber = antd.InputNumber; +const Slider = antd.Slider; +const Tooltip = antd.Tooltip; +const TintShadeTool = React.createClass({ getInitialState() { return { result: '#2db7f5', @@ -238,9 +192,9 @@ let TintShadeTool = React.createClass({ }); return; } - let tintOrShade = this.state.value > 0 ? 'tint' : 'shade'; - let c = new Values(this.state.color); - let resultColor = c[tintOrShade](Math.abs(this.state.value)); + const tintOrShade = this.state.value > 0 ? 'tint' : 'shade'; + const c = new Values(this.state.color); + const resultColor = c[tintOrShade](Math.abs(this.state.value)); this.setState({ result: '#' + resultColor.hex, darkBackground: resultColor.getBrightness() < 50 @@ -254,7 +208,7 @@ let TintShadeTool = React.createClass({ }); }, render() { - var marks = { + const marks = { '-100': '加黑', '0': '原色', '100': '加白' @@ -278,39 +232,9 @@ let TintShadeTool = React.createClass({ } }); -ReactDOM.render(, document.getElementById('color-tint-shade-tool')); +ReactDOM.render(, mountNode); ````` - +Ant Design 专用色彩换算工具,用于解析类似 `#2db7f5 tint 80%` 的色彩标注。 + +less 或 scss 语言可以直接使用 `tint(#2db7f5, 80%)` 和 `shade(#2db7f5, 80%)` 的语法。 diff --git a/docs/spec/contrast.en-US.md b/docs/spec/contrast.en-US.md new file mode 100644 index 0000000000..455a1f7a93 --- /dev/null +++ b/docs/spec/contrast.en-US.md @@ -0,0 +1,50 @@ +--- +category: 十大原则 +order: 3 +title: Contrast +--- + +Contrast is one of the effective ways to add visual interest to your page, and to create an organizational hierarchy among different element that aid user in finding the information quickly. + +> Note: The important rule for contrast to be effective, it must be strong. Don't be wimp. + +--- + +## The Contrast of major and minor relationship + +good example +bad example + +In order to help user make a quick operation (something like the form,modal), a more important operation or a operation with higher frequency would be emphasized. + + +> Notes: ways of emphasizing are not just to intensify the key item. It could also weaken the other items. + + +
    + +Example of ignoring the primary and secondary sequence + +When there’s something needs users to make decision prudently, the system should remain neutral. It shouldn’t make the decision for users or lead them to make judgement. + +--- + +## Contrast of whole and part + +Example of whole and part 1 + +Example of whole and part 2 + +Taking advantage of changing the typesetting, the typeface and the size, we highlight the different levels and differentiate the ensemble and the part, which would make the page be more flexible and rhythmic. + +--- + +## Contrast of the state relation + +Example of static contrast + +Example of dynamic contrast + +Taking advantage of changing colors and adding assistant shapes, we realize the comparison of state relation, which could help users differentiate various information better + +The forms we usually see include 『static contrast』 and 『dynamic contrast』. diff --git a/docs/spec/contrast.zh-CN.md b/docs/spec/contrast.zh-CN.md new file mode 100644 index 0000000000..ba69cfa6d8 --- /dev/null +++ b/docs/spec/contrast.zh-CN.md @@ -0,0 +1,49 @@ +--- +category: 十大原则 +order: 3 +subtitle: Contrast +english: 对比 +--- + +对比是增加视觉效果最有效方法之一,同时也能在不同元素之间建立一种有组织的层次结构,让用户快速识别关键信息。 + +> 注:要实现有效的对比,对比就必须强烈,切不可畏畏缩缩。 + +--- + +## 主次关系对比 + +正确示例 +错误示例 + +为了让用户能在操作上(类似表单、弹出框等场景)快速做出判断, 来突出其中一项相对更重要或者更高频的操作。 + +> 注意:突出的方法,不局限于强化重点项,也可以是弱化其他项。 + +
    + +不区分主次的示例 + +在一些需要用户慎重决策的场景中,系统应该保持中立,不能替用户或者诱导用户做出判断。 + +--- + +## 总分关系对比 + +总分关系示例 1 + +总分关系示例 2 + +通过调整排版、字体、大小等方式来突出层次感,区分总分关系,使得页面更具张力和节奏感。 + +--- + +## 状态关系对比 + +静态对比示例 + +动态对比示例 + +通过改变颜色、增加辅助形状等方法来实现状态关系的对比,以便用户更好的区分信息。 + +常见类型有『静态对比』、『动态对比』。 diff --git a/docs/spec/direct.md b/docs/spec/direct.md new file mode 100644 index 0000000000..4130e3dbcf --- /dev/null +++ b/docs/spec/direct.md @@ -0,0 +1,66 @@ +--- +category: 十大原则 +order: 5 +subtitle: Make it Direct +english: 直截了当 +--- + +正如 Alan Cooper 所言:『需要在哪里输出,就要允许在哪里输入』。这就是直接操作的原理。eg:不要为了编辑内容而打开另一个页面,应该直接在上下文中实现编辑。 + +--- + +## 页内编辑 + +单击编辑示例 + +单字段行内编辑 + +当『易读性』远比『易编辑性』重要时,可以使用『单击编辑』。 + +
    + +文字链/图标编辑示例 + +当『易读性』为主,同时又要突出操作行的『易编辑性』时,可使用『文字链/图标编辑』。 + +
    + +多字段行内编辑示例 + +多字段行内编辑 + +>注:在『多字段行内编辑』的情况下,显示的内容和编辑时所需的字段会存在比较大的差异,所以更需要『巧用过渡』原则中的[『解释刚刚发生了什么』](../spec/transition#解释刚刚发生了什么)来消除这种视觉影响。 + +
    + +更多有关『页内编辑』的模式,可查看[『模式/表格/交互』](/docs/pattern/table#模块编辑)中的内容。 + +
    + +--- + +## 利用拖放 + +拖放列表示例 + +拖放列表 + +只能限制在一个维度(上/下或者左/右)进行拖放。 + +
    + +拖放图片/文件示例 + +拖放图片/文件 + +
    + +

    拖放对象(敬请期待)

    + +

    拖放多个对象(敬请期待)

    + +
    + +--- + +

    直接选择(敬请期待)

    diff --git a/docs/spec/easing.md b/docs/spec/easing.md deleted file mode 100644 index 0d9bade216..0000000000 --- a/docs/spec/easing.md +++ /dev/null @@ -1,102 +0,0 @@ -# 自然运动 - -- category: 动画 -- order: 0 - ---- - -现实物体照着一定节奏移动,并不是一开始就移动很快的。 - -当我们打开现代家具的门或抽屉时,首先会让它加速,然后慢下来。 - -当电梯开关门时,它在打开或关闭时都有一段缓冲带,是先加速,然后慢下来。 - -当某个东西往下掉时,首先是越掉越快,撞到地上后回弹,最终才又碰触地板。 - - -### 质量和重量 - -在物理世界里,是以力量附加到物体对象里,加上自身的质量才完成一段动画。力量的持续时间决定物体的加速,减速与改变方向。 - -动画停止与启动都不是瞬间完成的,因它需要一段缓冲的时间来加速或减速,因此,当动画有突然启动,停止或改变方向,都会显得很不自然。 - -> 以下图形,红色线与圆环为不推荐示例,蓝色为推荐示例。 - -#### 自然缓动 - -不要用直线缓动 Linear 做物体位移或出入动画的缓动;注:Linear 函数可做循环动画函数。 - -如下图所示,在没有缓动的情况下启动与停止都显得突兀,感觉动画还没结束就停止了,所以在物体运动中避免直线运动。 - - -
    - - -上图所示缓动函数:红 `Linear`,蓝 `ease-in-out`。 - - -#### 出入动画 - -不要做单向动画,进场后不做出场,直接消失元素或回到原点,会造成整体效果不协调和用户视觉上的闪现或其它不好的效果。 - -所以有动画的进场必须要有动画的出场,包括导航上的动画。 - -
    - - -上图所示缓动函数:红 `ease-in-out`,蓝 `ease-in-out-cubic`。 - - -##### 场外出入 - -场外出入需要考虑力量与引力的关系,如向空中抛物体时,物体的初速度是最快的。 - -上升过程中物体受到引力的作用,速度也随之匀速降低,高度在时间轴上呈现抛物线弧度。 - -物体达到最高点速度减为 0,然后物体下降,速度受重力作用匀速增加。 - -所以最高点前后都有一定缓冲带,曲线呈现抛物线的规律。不要做图示红色球体下降时的缓动。 - -
    - - -上图所示缓动函数:红 `ease-out` `ease-out`,蓝 `ease-out-cubic` `ease-in-cubic`。 - -示例组件:[Message 全局提示](/components/message/),[Dropdown 下拉菜单](/components/dropdown/)。 - -#### 弹性动画 - -1. 如蹦极跳下来时,刚跳下时速度很快,到达绳子的长度后,由于物体的重量再将绳子拉长再反弹,弹动几次后才停下。 - - 动画里也由质量来决定它的反弹,一般元素最好只弹动一次就够了,收回时可以用向下浮动再上拉或直接前缓动,可适用在下拉框与弹出元素。 - -2. 如球类物体掉地上的后,反弹几次后停止。 - - 弹性动画最好结合 alpha。 - -> 慎用 Bounce 或 Elastic,这两种适用在特殊元素下,一般 back 即可满足页面上元素的弹动; - -
    - - -上图所示缓动函数:绿 `easeOutBounce` `easeOutElastic`(css 需自配), 蓝 `ease-out-back` `ease-in-back`。 - - -## 缓动函数 - -Ant Design 提供了一套缓动函数规范动画行为供组件使用。 - -|名称 |参数 |说明 | -|-------------------|------------------------------------------|---------------------------| -|@ease-out | `cubic-bezier(0.215,0.61,0.355,1);` |默认后缓动 | -|@ease-in | `cubic-bezier(0.55,0.055,0.675,0.19);`|默认前缓动 | -|@ease-in-out | `cubic-bezier(0.645,0.045,0.355,1);` |默认前后缓动 | -|@ease-out-back | `cubic-bezier(0.18,0.89,0.32,1.28);` |结束回动 | -|@ease-in-back | `cubic-bezier(0.6,-0.3,0.74,0.05);` |开始回动 | -|@ease-in-out-back | `cubic-bezier(0.68,-0.55,0.27,1.55);` |前后回动 | -|@ease-out-circ | `cubic-bezier(0.08,0.82,0.17,1);` |圆形后缓动 | -|@ease-in-circ | `cubic-bezier(0.6,0.04,0.98,0.34);` |圆形前缓动 | -|@ease-in-out-circ | `cubic-bezier(0.78,0.14,0.15,0.86);` |圆形前后缓动 | -|@ease-out-quint | `cubic-bezier(0.23, 1, 0.32, 1);`| quint 后缓动| -|@ease-in-quint | `cubic-bezier(0.755, 0.05, 0.855, 0.06);`|quint 前缓动| -|@ease-in-out-quint | `cubic-bezier(0.86, 0, 0.07, 1);`| quint 前后缓动| diff --git a/docs/spec/feature.md b/docs/spec/feature.md new file mode 100644 index 0000000000..f86af37251 --- /dev/null +++ b/docs/spec/feature.md @@ -0,0 +1,109 @@ +--- +order: 1 +english: 三大特性 +--- + +与众不同的是,Ant Design 不但追求『用户』的使用体验,还追求『设计者』的使用体验,真真正正贯彻和践行『以人为本』的设计理念。 + +
    +
    + +
    微小
    +
    致力于微小而美好的改变,力求在细节上精益求精,不仅让业务产品更加实用和可靠,而且还能让『用户』感到小惊喜。
    +
    +
    + +
    确定
    +
    制定通俗而科学的设计原则、运用面向对象的方法、使用一致的文档沟通机制,给予研发团队一种高确定性、低熵值的研发状态。
    +
    +
    + +
    幸福
    +
    不苛求简单,但是力求让『用户』和『设计者』流畅的完成目标,并带着成功和满足离开。
    +
    +
    + + + +--- + +## 微小 + +### 微创新 + +数值输入框示例 + +分页示例 + +字数校验框示例 + +『不同』不一定『更好』,但是『更好』一定『不同』。不断追求细节上的『更好』,使得我们的组件和同类产品都不一样,自然而然的『不同』。 + +### 集成创新 + +填空示例 + +带图表的表格 + +选择合适的组件进行组合和集成,形成优势互补的创新过程,来满足多变的业务需求。 + +--- + +## 确定 + +### 面向对象的方法 + +色值换算工具示例 + +排版规则示例 + +操作反馈 - 正确示例 + +操作反馈 - 错误示例 + +探索设计规律,并将其抽象成『对象』,增强界面设计的灵活性和可维护性,同时也减少『设计者』的主观干扰,从而降低系统的不确定性。 + +### 通俗而科学的设计原则 + +详见[『十大原则』](/docs/spec/principle)。 + +--- + +## 幸福 + +### 用户的幸福 + +用户的幸福示例 + +漂亮的组件、精致的排版、流畅的动画等的元素,使用户在『本能层次』中产生积极反应; + +良好的功能、性能和可用性,使用户在『行为层次』中产生积极反应; + +自我形象、个人满足和美好记忆,使用户在『反思层次』中体验思想和情感的交融。 + +### 设计者的幸福 + +设计者的幸福示例 + +从『无』到『有』时,提供一整套设计解决方案,帮助『设计者』将商业想法快速形成产品并推向市场,快速、低成本试错。 + +从『有』到『优』时,提供一系列自定义建议,帮助『设计者』塑造产品个性并提升整体体验,服务海量用户。 diff --git a/docs/spec/font.md b/docs/spec/font.md index 0954633e8e..3c92a114bd 100644 --- a/docs/spec/font.md +++ b/docs/spec/font.md @@ -1,12 +1,13 @@ -# 字体 - -- category: 基础 -- order: 0 - +--- +category: 设计基础 +order: 0 +english: 字体 --- 跨平台的字体设定,力求在各个操作系统下都有最佳展示效果。 +--- + ## 字体家族 - 中文字体族: @@ -25,29 +26,29 @@ font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Micros ## 字体使用规范 -
    -
    -
    +
    +
    +
    主标题
    -

    +

    我是标题 加粗 #666 16px

    -
    +
    次级标题
    -

    +

    我是标题 加粗 #666 14px

    -
    +
    小标题
    -

    +

    我是标题 加粗 #666 @@ -89,28 +90,28 @@ font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Micros

    -
    -
    +
    +
    Main Head
    -

    +

    I am example text bold #666 16px

    -
    +
    Sub Head
    -

    +

    I am example text bold #666 14px

    -
    +
    Small Head
    -

    +

    I am example text bold #666 @@ -174,19 +175,25 @@ font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Micros font-size: 14px; width: 100px; } -.font-text { +.font-type h1, +.font-type h2, +.font-type h3, +.font-type .font-text { width: 300px; } -.font-text span { - margin-right: 8px; +.font-type h1 span, +.font-type h2 span, +.font-type h3 span, +.font-type .font-text span { + margin-right: 0.6em; } -.head-1 h1 { +.font-type h1 { font-size: 16px; } -.head-2 h2 { +.font-type h2 { font-size: 14px; } -.head-3 h3 { +.font-type h3 { font-size: 12px; } .disabled-text .font-text { diff --git a/docs/spec/introduce.en-US.md b/docs/spec/introduce.en-US.md new file mode 100644 index 0000000000..d9e542f725 --- /dev/null +++ b/docs/spec/introduce.en-US.md @@ -0,0 +1,35 @@ +--- +order: 0 +title: Ant Design +--- + +
    + +
    + +In the development process of middleware products, many different design specs and implementations would be involved, which might cause designers and developers difficulties and duplication and reduce the efficiency of development. After massive project practice and summaries, Ant Design, a design language for middleware, is refined by Experience Technology Department of Ant Financial, which aims to uniform the user interface specs for middleware projects, reduce the unnecessary cost of design differences and implementation and liberate the resources of design and front-end development. + +Ant Design, created specially for middleware design, is committed to improving the experience of users and product designers. User interface designers and user experience designers, collectively, are considered as product designers, and the boundaries of product managers, interaction designers, visual designers, front-end developers and develop engineers are blurred. Taking advantage of unitary specifications, Ant Design makes design and prototype more simple and accessible for all project members, which comprehensively promotes experience and development efficiency of middleware products. + +--- +## Our Service Recipients + +- Ant Financial +- Alibaba +- Koubei +- China Internet Plus +- Didi + +> If your company or products use Ant Design, welcome to click [here](https://github.com/ant-design/ant-design/issues/477) to leave a message. + +## Front-end Implementation + +[React](http://facebook.github.io/react/) is used to encapsulate a library of Ant Design components. Any other version of frameworks to implement is also welcome. + +- [Ant Design of React](/docs/react/introduce)(official implementation) +- [vue-antd](https://github.com/okoala/vue-antd) +- [antd-ember](https://github.com/idcos/antd-ember) + +## How to Contribute + +Welcome to contribute to Ant Design on Github. If you have any suggestions for improvement, questions,or concerns, do not hesitate to create [Pull Request](https://github.com/ant-design/ant-design/pulls) or advice us [here](https://github.com/ant-design/ant-design/issues). diff --git a/docs/spec/introduce.md b/docs/spec/introduce.md deleted file mode 100644 index d26ad09ae7..0000000000 --- a/docs/spec/introduce.md +++ /dev/null @@ -1,32 +0,0 @@ -# Ant Design - -- category: 0 -- order: 0 - ---- - -Ant Design 是一个 UI 设计语言,是一套提炼和应用于企业级中后台产品的交互语言和视觉体系。 - - - -Ant Design 源自蚂蚁金服体验技术部的后台产品开发。在中后台产品设计中,通常有很多类似的页面和控件,不同的产品会出现不同的规范和实现,给设计师和工程师带来很多困扰和重复建设,降低企业后台的整体研发效率。我们的设计师和前端工程师经过大量的项目实践和总结,希望能沉淀出一套企业级的交互视觉规范,统一中后台项目的前端 UI 设计,屏蔽各种不必要的设计差异和前端实现成本,解放设计和前端开发资源。 - -整套设计规范还在持续整理和完善中。 - -## 谁在使用 - -- 蚂蚁金服 -- 口碑 - -> 如果你的公司和产品使用了 Ant Design,欢迎到 [这里](https://github.com/ant-design/ant-design/issues/477) 留言。 - -## 前端实现 - -我们采用 [React](http://facebook.github.io/react/) 封装了一套 Ant Design 的组件库,也欢迎社区其他框架的实现版本。 - -- [Ant Design of React](/docs/react/introduce)(官方实现) -- [vue-antd](https://github.com/okoala/vue-antd) - -## 如何贡献 - -我们欢迎任何形式的贡献,有任何建议或意见您可以进行 [Pull Request](https://github.com/ant-design/ant-design/pulls),或者给我们[提问](https://github.com/ant-design/ant-design/issues)。 diff --git a/docs/spec/introduce.zh-CN.md b/docs/spec/introduce.zh-CN.md new file mode 100644 index 0000000000..0322885e46 --- /dev/null +++ b/docs/spec/introduce.zh-CN.md @@ -0,0 +1,37 @@ +--- +order: 0 +title: Ant Design +--- + +
    + +
    + +在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,给设计师和工程师带来很多困扰和重复建设,大大降低了产品的研发效率。我们(蚂蚁金服体验技术部)经过大量的项目实践和总结,沉淀出一个中台设计语言 Ant Design。旨在统一中台项目的前端 UI 设计,屏蔽不必要的设计差异和实现成本,解放设计和前端的研发资源。 + +Ant Design 是一个致力于提升『用户』和『设计者』使用体验的中台设计语言。它模糊了产品经理、交互设计师、视觉设计师、前端工程师、开发工程师等角色边界,将进行 UE 设计和 UI 设计人员统称为『设计者』,利用统一的规范进行设计赋能,全面提高中台产品体验和研发效率。 + +--- + +## 谁在使用 + +- 蚂蚁金服 +- 阿里巴巴 +- 口碑 +- 新美大 +- 滴滴 + +> 如果你的公司和产品使用了 Ant Design,欢迎到 [这里](https://github.com/ant-design/ant-design/issues/477) 留言。 + +## 前端实现 + +我们采用 [React](http://facebook.github.io/react/) 封装了一套 Ant Design 的组件库,也欢迎社区其他框架的实现版本。 + +- [Ant Design of React](/docs/react/introduce)(官方实现) +- +-
    +- + +## 如何贡献 + +我们欢迎任何形式的贡献,有任何建议或意见您可以进行 [Pull Request](https://github.com/ant-design/ant-design/pulls),或者给我们 [提问](https://github.com/ant-design/ant-design/issues)。 diff --git a/docs/spec/invitation.en-US.md b/docs/spec/invitation.en-US.md new file mode 100644 index 0000000000..093bbc9957 --- /dev/null +++ b/docs/spec/invitation.en-US.md @@ -0,0 +1,81 @@ +--- +category: 十大原则 +order: 8 +title: Provide an Invitation +--- + +A common problem with many of these rich interactions (e.g. Drag and Drop, Inline Editing, and Contextual Tools) is their lack of discoverability. Providing an invitation to the user is one of the keys to successful interactive interfaces. + +Invitations are the prompts and cues that lead users through an interaction. They often include just-in-time tips or visual affordances that hint at what will happen next in the interface. + +> ** Signifiers ** are signals, communication devices. These signs tell you about the possible actions; what to do, and where to do it. Signifiers are often visible, audible or tangible, from the Design of Everyday Things. + +> ** Affordances ** are the relationships (read: possible actions) between an object and an entity (most often a person). The presence of an affordance is determined by the properties of the object and of the abilities of the entity who's interacting with the object, from the Design of Everyday Things. + +--- + + +## Static Invitations + +By providing cues for interaction directly on the page we can statically indicate to the user the expected interface behavior. Static Invitations provide cues directly on the page. + +
    + +example of Text Invitation + +example of Blank Slate Invitation + +example of Unfinished Invitation + +Call to Action Invitations are generally provided as static instructions on the page. But visually they can be provided in many different ways such as Text Invitation, Blank Slate Invitation and Unfinished Invitation. + +
    + +example 1 of Tour Invitation + +example 2 of Tour Invitation + +Tour invitation can be a nice way to explain design changes to a web application, especially for a well-designed interface. But providing tours will not solve the real problems an interface may have during interaction. + +>Note that make Tour Invitations short and simple, easy to exit, and clear to restart. + + +
    + +--- + +## Dynamic Invitations + +Dynamic Invitations engage users at the point of the interaction and guide them through the next step of interaction. + +
    + +example 1 of Hover Invitation + +example 2 of Hover Invitation + +Hover Invitation: Provide an invitation during mouse hover. + + +
    + +example of Inference Invitation + +Inference Invitation: Use visual inferences during interaction to cue users as to what the system has inferred about their intent. + +
    + +example of More Content Invitation + +More Content Invitation: Indicate that there is more content on the page. + + +
    + +
    + +

    Affordance Invitation (coming soon)

    + +
    + +

    Drag and Drop Invitation (coming soon)

    diff --git a/docs/spec/invitation.zh-CN.md b/docs/spec/invitation.zh-CN.md new file mode 100644 index 0000000000..afbc9c625f --- /dev/null +++ b/docs/spec/invitation.zh-CN.md @@ -0,0 +1,83 @@ +--- +category: 十大原则 +order: 8 +subtitle: Provide Invitation +english: 提供邀请 +--- + +很多富交互模式(eg:『拖放』、『行内编辑』、『上下文工具』)都有一个共同问题,就是缺少易发现性。所以『提供邀请』是成功完成人机交互的关键所在。 + +邀请就是引导用户进入下一个交互层次的提醒和暗示,通常包括意符(eg:实时的提示信息)和可供性,以表明在下一个界面可以做什么。当可供性中可感知的部分(Perceived Affordance)表现为意符时,人机交互的过程往往更加自然、顺畅。 + +> ** 意符(Signifiers)** :一种额外的提示,告诉用户可以采取什么行为,以及应该怎么操作;必须是可感知(eg:视觉、听觉、触觉等)。——摘自《设计心理学 1 》 + +> ** 可供性(Affordance)** :也被翻译成『示能』,由 James J. Gibson 提出,定义为物品的特性与决定物品用途的主体能力之间的关系;其中部分可感知(此部分定义为 Perceived Affordance),部分不可感知。——摘自《设计心理学 1 》 + +--- + + +## 静态邀请 + +指通过可视化技术在页面上提供引导交互的邀请。 + +
    + +文本邀请示例 + +白板式邀请示例 + +未完成邀请示例 + +引导操作邀请:一般以静态说明形式出现在页面上,不过它们在视觉上也可以表现出多种不同样式。 +常见类型:『文本邀请』、『白板式邀请』、『未完成邀请』。 + +
    + +漫游探索邀请示例 1 + +漫游探索邀请示例 2 + +漫游探索邀请:是向用户介绍新功能的好方法,尤其是对于那些设计优良的界面。但是它不是『创口贴』,仅通过它不能解决界面交互的真正问题。 + +>注:保持漫游过程简单,让用户容易退出和重新启动;保持内容简明扼要,与功能密切相关。 + + +
    + +--- + +## 动态邀请 + +指以响应用户在特定位置执行特定操作的方式,提供特定的邀请。 + +
    + +悬停邀请示例 1 + +悬停邀请示例 2 + +悬停邀请:在鼠标悬停期间提供邀请。 + + +
    + +推论邀请示例 + +推论邀请:用于交互期间,合理推断用户可能产生的需求。 + +
    + +更多内容邀请示例 + +更多内容邀请:用于邀请用户查看更多内容。 + + +
    + +
    + +

    预期功能邀请 (敬请期待)

    + +
    + +

    拖放邀请 (敬请期待)

    diff --git a/docs/spec/layout/demo/aside-collapse.md b/docs/spec/layout/demo/aside-collapse.md new file mode 100644 index 0000000000..d61761200e --- /dev/null +++ b/docs/spec/layout/demo/aside-collapse.md @@ -0,0 +1,218 @@ +--- +order: 4 +title: 可收起展开的侧边导航 +--- + +页面横向空间有限时使用。侧边导航默认收起,点击底部按钮时展开。 + +````jsx +import { Menu, Breadcrumb, Icon } from 'antd'; +import BrowserDemo from 'site/theme/template/BrowserDemo'; +const SubMenu = Menu.SubMenu; + +const AsideCollapse = React.createClass({ + getInitialState() { + return { + collapse: true, + }; + }, + onCollapseChange() { + this.setState({ + collapse: !this.state.collapse, + }) + }, + render() { + const collapse = this.state.collapse; + return ( +
    + +
    +
    +
    + + 首页 + 应用列表 + 某应用 + +
    +
    +
    +
    + 内容区域 +
    +
    +
    +
    + Ant Design 版权所有 © 2015 由蚂蚁金服体验技术部支持 +
    +
    +
    + ); + }, +}); + +ReactDOM.render(, mountNode); +```` + +````css +.ant-layout-aside { + position: relative; + min-height: 100%; +} + +.ant-layout-aside .ant-layout-logo { + width: 150px; + height: 32px; + background: #333; + border-radius: 6px; + margin: 16px 24px 16px 28px; + transition: all 0.3s ease; +} + +.ant-layout-aside-collapse .ant-layout-logo { + width: 32px; + margin: 16px; + transition: all 0.3s ease; +} + +.ant-layout-aside .ant-layout-sider { + width: 224px; + background: #404040; + position: absolute; + overflow: visible; + padding-bottom: 24px; + height: 100%; + transition: all 0.3s ease; +} + +.ant-layout-aside-collapse .ant-layout-sider { + width: 64px; + transition: all 0.3s ease; +} + +.ant-layout-aside .ant-layout-sider > .ant-menu { + margin-bottom: 20px; +} + +.ant-layout-aside .ant-layout-sider > .ant-menu > .ant-menu-item { + margin: 16px 0; +} + +.ant-layout-aside .ant-layout-sider > .ant-menu > .ant-menu-item .nav-text { + vertical-align: baseline; + display: inline-block; +} + +.ant-layout-aside .ant-layout-sider > .ant-menu > .ant-menu-item > .anticon { + transition: font-size .3s; +} + +.ant-layout-aside-collapse .ant-layout-sider > .ant-menu > .ant-menu-item { + transition: all 0s ease; +} + +.ant-layout-aside-collapse .ant-layout-sider > .ant-menu > .ant-menu-item > .anticon { + font-size: 16px; + display: inline-block; +} + +.ant-layout-aside-collapse .ant-layout-sider > .ant-menu > .ant-menu-item .nav-text { + display: none; +} + +.ant-layout-aside-collapse .ant-layout-sider > .ant-menu > .ant-menu-item:hover { + background: #2db7f5; + color: #fff; + transition: all 0s ease; +} + +.ant-layout-aside-collapse .ant-layout-sider > .ant-menu > .ant-menu-item:hover .nav-text { + display: inline-block; + vertical-align: top; + background: #2db7f5; + color: #fff; + padding-right: 16px; + border-radius: 0 5px 5px 0; +} + +/* 实际使用中需要改成 position: fixed */ +.ant-layout-aside .ant-aside-action { + height: 42px; + width: 224px; + position: absolute; + bottom: 0; + background: #656565; + color: #fff; + text-align: center; + line-height: 42px; + cursor: pointer; + transition: all 0.3s ease; +} + +.ant-layout-aside-collapse .ant-aside-action { + width: 64px; + transition: all 0.3s ease; +} + +.ant-layout-aside .ant-layout-header { + background: #fff; + height: 64px; + border-bottom: 1px solid #e9e9e9; +} + +.ant-layout-aside .ant-layout-breadcrumb { + margin: 7px 0 -17px 24px; +} + +.ant-layout-aside .ant-layout-main { + margin-left: 224px; + transition: all 0.3s ease; +} + +.ant-layout-aside-collapse .ant-layout-main { + margin-left: 64px; + transition: all 0.3s ease; +} + +.ant-layout-aside .ant-layout-container { + margin: 24px 16px; +} + +.ant-layout-aside .ant-layout-content { + background: #fff; + padding: 24px; +} + +.ant-layout-aside .ant-layout-footer { + height: 64px; + line-height: 64px; + text-align: center; + font-size: 12px; + color: #999; + background: #fff; + border-top: 1px solid #e9e9e9; + width: 100%; +} +```` diff --git a/docs/spec/layout/demo/aside.md b/docs/spec/layout/demo/aside.md index 302d52e856..f1d00763b7 100644 --- a/docs/spec/layout/demo/aside.md +++ b/docs/spec/layout/demo/aside.md @@ -1,15 +1,15 @@ -# 侧边导航 - -- order: 2 +--- +order: 2 +title: 侧边导航 +--- 顶级导航在侧边栏。 侧边导航在页面布局上采用的是左右的结构,一般主导航放置于页面的左侧固定位置,辅助菜单放置于工作区顶部。内容根据浏览器终端进行自适应,能提高横向空间的使用率,但是整个页面排版不稳定。侧边导航的模式层级扩展性强,一、二、三级导航项目可以更为顺畅且具关联性的被展示,同时侧边导航可以固定,使得用户在操作和浏览中可以快速的定位和切换当前位置,有很高的操作效率。但这类导航横向页面内容的空间会被牺牲一部份。 ---- - ````jsx import { Menu, Breadcrumb, Icon } from 'antd'; +import BrowserDemo from 'site/theme/template/BrowserDemo'; const SubMenu = Menu.SubMenu; ReactDOM.render( diff --git a/docs/spec/layout/demo/ceiling.md b/docs/spec/layout/demo/ceiling.md index 9675c8fd7f..2b15ce835c 100644 --- a/docs/spec/layout/demo/ceiling.md +++ b/docs/spec/layout/demo/ceiling.md @@ -1,15 +1,15 @@ -# 吊顶规范 - -- order: 3 +--- +order: 3 +title: 吊顶规范 +--- 吊顶一般用于跨系统/应用场景,可以放置统一的登录/帮助信息。 吊顶背景深色,高度 `30px`,和浅色调的主导航配合使用。 ---- - ````jsx import { Menu, Breadcrumb } from 'antd'; +import BrowserDemo from 'site/theme/template/BrowserDemo'; ReactDOM.render( diff --git a/docs/spec/layout/demo/top-aside.md b/docs/spec/layout/demo/top-aside.md index b779b093c8..4295dbaead 100644 --- a/docs/spec/layout/demo/top-aside.md +++ b/docs/spec/layout/demo/top-aside.md @@ -1,13 +1,13 @@ -# 顶部导航 + 侧边栏 - -- order: 1 +--- +order: 1 +title: 顶部导航 + 侧边栏 +--- 顶级导航在头部,次级导航在侧边栏。 ---- - ````jsx import { Menu, Breadcrumb, Icon } from 'antd'; +import BrowserDemo from 'site/theme/template/BrowserDemo'; const SubMenu = Menu.SubMenu; ReactDOM.render( diff --git a/docs/spec/layout/demo/top.md b/docs/spec/layout/demo/top.md index d31adb56b6..c9d942c012 100644 --- a/docs/spec/layout/demo/top.md +++ b/docs/spec/layout/demo/top.md @@ -1,6 +1,7 @@ -# 顶部导航 - -- order: 0 +--- +order: 0 +title: 顶部导航 +--- 一二级导航都在顶部。 @@ -8,10 +9,9 @@ > `` 做演示用,无须复制。 ---- - ````jsx import { Menu, Breadcrumb } from 'antd'; +import BrowserDemo from 'site/theme/template/BrowserDemo'; ReactDOM.render( diff --git a/docs/spec/layout/index.md b/docs/spec/layout/index.md index bf41733f15..0681ab0898 100644 --- a/docs/spec/layout/index.md +++ b/docs/spec/layout/index.md @@ -1,10 +1,10 @@ -# 常用布局 - -- template: component -- category: 基础 -- order: 4 -- cols: 1 - +--- +template: component +category: 设计基础 +order: 4 +cols: 1 +chinese: 常用布局 +english: Layout --- 布局和导航是产品的骨架,是页面的重要构成模式之一,是作为后续展开页面设计的基础,可以为产品奠定交互和视觉风格。 diff --git a/docs/spec/lightweight.en-US.md b/docs/spec/lightweight.en-US.md new file mode 100644 index 0000000000..10c02e16c5 --- /dev/null +++ b/docs/spec/lightweight.en-US.md @@ -0,0 +1,78 @@ +--- +category: 十大原则 +order: 6 +title: Keep it Lightweight +--- + +Fitts’s Law is an ergonomic principle that ties the size of a target and its contextual proximity to ease of use.In other words, if a tool is close at hand and large enough to target, then we can improve the user’s interaction. Putting tools in context makes for lightweight interaction. + + +> +>** Fitts's Law **: The time to acquire a target is a function of the distance to and size of the target. It is proportional to the distance to the target and inversely proportional to the width of the target. + +--- + +## Always-Visible Tools + + +example of Always-Visible Tools, from Zhihu + +If an action is critical, expose it directly in the interface and keep it always visible. + +
    + +--- + +## Hover-Reveal Tools + + +example of Hover-Reveal Tools + +Instead of making Contextual Tools always visible, we can show them on demand. One way to do this is to reveal the tools when the user pauses the mouse over an object. + +
    + +--- + +## Toggle-Reveal Tools + + +example of Toggle-Reveal Tools + +Toggle a tool mode for an area or page when the actions are not the main flow. The tools to accomplish this are revealed on the activation of the toogle. + + +
    + +--- + +## Interaction in Context + +good example + +good example + +bad example + + +If the actions are secondary or alternative, hide Contextual Tools in the user flow, in order to keep visual clutter to a minimum, reduce the load of cognition burden and bring a surprise. + +Some relative knowledge of [Providing an Invitation](/docs/spec/invitation) can also be applied. + +
    + +--- + +## Visible Area ≠ Clickable Area + +example of hypertext hot spot + +The clickable area of hypertext is affected by the length of the stirng in a cell. The while cell can be set to a hot spot in order to be triggered easier. + +
    + +example of button hot spot + +Increase the clickable hot spot to strenthen the responsiveness rather than increase the size of the button. + +>Note that it is especially suited for Mobile. diff --git a/docs/spec/lightweight.zh-CN.md b/docs/spec/lightweight.zh-CN.md new file mode 100644 index 0000000000..b805a7c4cb --- /dev/null +++ b/docs/spec/lightweight.zh-CN.md @@ -0,0 +1,77 @@ +--- +category: 十大原则 +order: 6 +subtitle: Keep it Lightweight +english: 简化交互 +--- + +根据费茨法则(Fitts's Law)所描述的,如果用户鼠标移动距离越少、对象相对目标越大,那么用户越容易操作。通过运用上下文工具(即:放在内容中的操作工具),使内容和操作融合,从而简化交互。 + +> ** 费茨法则 ** :到达目标的时间是到达目标的距离与目标大小的函数,具体:。其中:1.设备当前位置和目标位置的距离(D);2.目标的大小(W)。距离越长,所用时间越长;目标越大,所用时间越短。 + +--- + +## 实时可见工具 + + +实时可见工具示例 --摘自知乎 + +如果某个操作非常重要,就应该把它放在界面中,并实时可见。 + +
    + +--- + +## 悬停即现工具 + + +悬停即现工具示例 + +如果某个操作不那么重要,或者使用『实时可见工具』过于啰嗦会影响用户阅读时,可以在悬停在该对象上时展示操作项。 + +
    + +--- + +## 开关显示工具 + + +开关显示工具示例 + +如果某些操作只需要在特定模式时显示,可以通过开关来实现。 + + +
    + +--- + +## 交互中的工具 + +推荐示例 + +推荐示例 + +不推荐示例 + + +如果操作不重要或者可以通过其他途径完成时,可以将工具放置在用户的操作流程中,减少界面元素,降低认知负担,给用户小惊喜。 + +此处也可以运用[『提供邀请』](/docs/spec/invitation) 相关的知识点。 + +
    + +--- + +## 可视区域 ≠ 可点击区域 + +文字链热区示例 + +在使用 Table 时,文字链的点击范围受到文字长短影响,可以设置整个单元格为热区,以便用户触发。 + +
    + +按钮热区示例 + +当需要增强按钮的响应性时,可以通过增加用户点击热区的范围,而不是增大按钮形状,从而增强响应性,又不缺失美感。 + +>注:在移动端尤其适用。 diff --git a/docs/spec/motion.md b/docs/spec/motion.md index 3c4b02246a..82690ac8d7 100644 --- a/docs/spec/motion.md +++ b/docs/spec/motion.md @@ -1,43 +1,22 @@ -# 组件动画 - -- category: 动画 -- order: 2 - +--- +category: 设计基础 +order: 5 +chinese: 组件动画 +english: Motion --- -Ant Design 提供了一些预设的组件动画样式。 +依据『巧用过渡』的设计原则,Ant Design 提供了一些预设的组件动画和缓动函数。 -
    +> 示例延长了动画时长以便展示。 -## 组件动画 +`````__react +const cssAnimation = require('css-animation'); +const antd = require('antd'); +const Select = antd.Select; +const Option = Select.Option; +const OptGroup = Select.OptGroup; -通过设置组件的 `transitionName` 指定组件动画。 - -| 组件 | 中文名 | 采用动画 | -|--------------|---------------------|-------------------------------------------------| -| popover | 气泡浮出 | `zoom-up` `zoom-down` `zoom-left` `zoom-right` | -| popconfirm | 气泡确认 | `zoom-up` `zoom-down` `zoom-left` `zoom-right` | -| tooltip | 文字提示 | `zoom-up` `zoom-down` `zoom-left` `zoom-right` | -| modal | 弹出框 | `zoom` | -| confirm | 弹出确认框 | `zoom` | -| message | 信息提示条 | `move-up` | -| notification | 通知框 | `move-right` & `slide-up` | -| dropdown | 下拉菜单 | `slide-up` | -| select | 选择框 | `slide-up` | -| datepicker | 日期选择框 | `slide-up` | -| alert | 警告提示 | `slide-up` | -| menu | 导航菜单 | `slide-up` | -| datepicker | 日期选择框 | `slide-up` | - - -`````jsx -var cssAnimation = require('css-animation'); -var Select = antd.Select; -var Option = Select.Option; -var OptGroup = Select.OptGroup; - - -var motions = []; +let motions = []; motions = motions.concat([[{ name: '淡入', value: 'fade', @@ -188,30 +167,29 @@ motions = motions.concat([[{ direction: 'enter', type: '其他' }]]); -var leave='-leave'; + +var leave = '-leave'; var Test = React.createClass({ handleChange(e) { var value = e; - if(value){ - this.demoNode.style.visibility=''; + if (value) { + this.demoNode.style.visibility = ''; cssAnimation(this.demoNode, value, () => { - if(value.slice(-leave.length)===leave){ - this.demoNode.style.visibility='hidden'; + if (value.slice(-leave.length) === leave) { + this.demoNode.style.visibility = 'hidden'; } }); } }, - componentDidMount() { this.demoNode = ReactDOM.findDOMNode(this.refs.demo); }, - render() { - var options = [].concat(motions.map(function (m) { - var opts = m.map(function (m2) { - return + const options = [].concat(motions.map(function (m, groupIndex) { + const opts = m.map(function (m2, optIndex) { + return }); - return {opts}; + return {opts}; })); return
    @@ -220,39 +198,50 @@ var Test = React.createClass({
    -
    ; } }); -ReactDOM.render(, document.getElementById('components-motion-demo-basic')); +ReactDOM.render(, mountNode); ````` - +## 组件动画 +| 组件 | 中文名 | 采用动画 | +|--------------|--------------------|-------------------------------------------------| +| Popover | 气泡浮出 | `zoom-up` `zoom-down` `zoom-left` `zoom-right` | +| Popconfirm | 气泡确认 | `zoom-up` `zoom-down` `zoom-left` `zoom-right` | +| Tooltip | 文字提示 | `zoom-up` `zoom-down` `zoom-left` `zoom-right` | +| Modal | 弹出框 | `zoom` | +| Badge | 徽标数 | `zoom` | +| message | 信息提示条 | `move-up` | +| notification | 通知框 | `move-right` & `slide-up` | +| Dropdown | 下拉菜单 | `slide-up` | +| Select | 选择框 | `slide-up` | +| Cascader | 级联选择框 | `slide-up` | +| TreeSelect | 树选择框 | `slide-up` | +| DatePicker | 日期选择框 | `slide-up` | +| TatePicker | 日期选择框 | `slide-up` | +| Alert | 警告提示 | `slide-up` | +| Menu | 导航菜单 | `slide-up` | + +在 React 的前端实现中,可以使用 [rc-animate](https://github.com/react-component/animate) 的 [transitionName](http://react-component.github.io/animate/examples/simple.html) 属性来给任意元素指定动画。 + +## 缓动函数 + +> `move@enter` 表示 `移动@进入`。 + +| 名称 | 参数 | 说明 | 应用动画 | +| -------------------|------------------------------------------|--------------------|------------| +| @ease-out | `cubic-bezier(0.215,0.61,0.355,1);` | 默认后缓动 | | +| @ease-in | `cubic-bezier(0.55,0.055,0.675,0.19);` | 默认前缓动 | | +| @ease-in-out | `cubic-bezier(0.645,0.045,0.355,1);` | 默认前后缓动 | | +| @ease-out-back | `cubic-bezier(0.18,0.89,0.32,1.28);` | 结束回动 | | +| @ease-in-back | `cubic-bezier(0.6,-0.3,0.74,0.05);` | 开始回动 | | +| @ease-in-out-back | `cubic-bezier(0.68,-0.55,0.27,1.55);` | 前后回动 | | +| @ease-out-circ | `cubic-bezier(0.08,0.82,0.17,1);` | 圆形后缓动 | `move@enter` `zoom@enter` | +| @ease-in-circ | `cubic-bezier(0.6,0.04,0.98,0.34);` | 圆形前缓动 | `move@leave` | +| @ease-in-out-circ | `cubic-bezier(0.78,0.14,0.15,0.86);` | 圆形前后缓动 | `zoom@leave` | +| @ease-out-quint | `cubic-bezier(0.23, 1, 0.32, 1);` | quint 后缓动 | `slide@enter` | +| @ease-in-quint | `cubic-bezier(0.755, 0.05, 0.855, 0.06);`| quint 前缓动 | `slide@leave` | +| @ease-in-out-quint | `cubic-bezier(0.86, 0, 0.07, 1);` | quint 前后缓动 | | diff --git a/docs/spec/page-transition.md b/docs/spec/page-transition.md deleted file mode 100644 index 6081466f32..0000000000 --- a/docs/spec/page-transition.md +++ /dev/null @@ -1,65 +0,0 @@ -# 互动转换 - -- category: 动画 -- order: 1 - ---- - -## 响应互动 - -响应交互一般是指我们在浏览页面时,点击元素时动画给我们视觉上的反馈,每个交互动效都能给我们带来不同视觉效果。 - -比如:按钮上的 hover 或 click 效果,随着你的鼠标事件而改变自身或增加元素在按钮上,或者折叠面板和弹出框,点击后给你呈现新加入的信息元素。 - -### 按钮反馈 - -
    -
    - - -## 转换动画 - -### 视觉连贯性三元素 - -- Adding:  新加入的信息元素应被告知如何使用,从页面转变的信息元素需被重新识别。 - -- Receding:  与当前页无关的信息元素应采用适当方式移除。 - -- Normal: 指那些从转场开始到结束都没有发生变化的信息元素。 - -### 可折叠面板 - -对于信息元素内容区域的延伸,显示信息元素和进一步内容对象之间的直接连接。 - - - 被展开的信息区域内容按照一定的路径依次进场。 - - - -
    -
    - - -### 弹出框动效 - -从一个触发点触发一个弹出框时,弹框从所触发区域弹出,且触发区域视觉上基本保持不变。这样让触发区域和弹出区域有所关联,提高用户对新内容的认知。 - -
    - -
    - - -### 页面转场 - -从内容A到内容B的转变过程时能有效的吸引用户注意力,突出视觉中心,提高整体视觉效果。 - - - 大页面转场可采用左出右入的形式。 - - - 小的信息元素排布或块状较多的情况下,根据一定的路径层次依次进场,区分维度层级,来凸显量级,来指引用户的视觉轨迹。 - - - -
    - -
    - -> 参考我们的进场组件案例。查看[进场动画组件(QueueAnim)](/components/queue-anim/) diff --git a/docs/spec/principle.en-US.md b/docs/spec/principle.en-US.md new file mode 100644 index 0000000000..2fc55c10d6 --- /dev/null +++ b/docs/spec/principle.en-US.md @@ -0,0 +1,46 @@ +--- +category: 十大原则 +order: 0 +title: Introduction +--- + +“It is more difficult to find good design than bad ones. ” Because good design are so natural, it can help users to easily meet the targets, so that the users are not aware of the existence of good design. + +After referring to the summary and reasoning of design principles from The Non-Designer’s Design Book” and “Designing Web Interfaces”, and combining with our practice and understanding of the team, we developed the following ten principles, which provide specific criteria and enlightenment of the solutions to problem for designers. + +> Note: the design principle is the abstraction and summary of representational design. However, users’ perception of the entire product is from global aspect to local aspect. So it is not desirable to ignore the full picture and only use partial principles. For the design principles, designers should learn them rationally, and then abandon them bravely. + +--- + + + +#### Ant Design 十大设计原则 + +- [亲密性 Proximity](/docs/spec/proximity) +- [对齐 Alignment](/docs/spec/alignment) +- [对比 Contrast](/docs/spec/contrast) +- [重复 Repetition](/docs/spec/repetition) +- [直截了当 Make it Direct](/docs/spec/direct) +- [简化交互 Keep it Lightweight](/docs/spec/lightweight) +- [足不出户 Stay on the Page](/docs/spec/stay) +- [提供邀请 Provide Invitation](/docs/spec/invitation) +- [巧用过渡 Use Transition](/docs/spec/transition) +- [即时反应 React Immediately](/docs/spec/reaction) + diff --git a/docs/spec/principle.zh-CN.md b/docs/spec/principle.zh-CN.md new file mode 100644 index 0000000000..39ae8599e0 --- /dev/null +++ b/docs/spec/principle.zh-CN.md @@ -0,0 +1,46 @@ +--- +category: 十大原则 +order: 0 +subtitle: Introduction +title: 引言 +--- + +『好设计比差设计更难发现』,因为好设计是如此的自然,帮助用户轻松的完成目标,以至于用户根本意识不到好设计的存在。 + +我们借鉴了《写给大家看的设计书》、《Web 界面设计》对设计原则的总结和推理,并结合我们团队的实践和理解,制定了以下十大原则,为『设计者』提供解决具体问题的准则和启示。 + +> 注:设计原则是对具象设计的抽象和总结,然而产品是一个整体,用户对整个产品的认知也是从全局到局部,所以忽略全局,只在局部套用原则是不可取的。对于这些原则,『设计者』应当理性地学会它,而后勇敢地抛弃它。 + +--- + + + +#### Ant Design 十大设计原则 + +- [亲密性 Proximity](/docs/spec/proximity) +- [对齐 Alignment](/docs/spec/alignment) +- [对比 Contrast](/docs/spec/contrast) +- [重复 Repetition](/docs/spec/repetition) +- [直截了当 Make it Direct](/docs/spec/direct) +- [简化交互 Keep it Lightweight](/docs/spec/lightweight) +- [足不出户 Stay on the Page](/docs/spec/stay) +- [提供邀请 Provide Invitation](/docs/spec/invitation) +- [巧用过渡 Use Transition](/docs/spec/transition) +- [即时反应 React Immediately](/docs/spec/reaction) diff --git a/docs/spec/proximity.en-US.md b/docs/spec/proximity.en-US.md new file mode 100644 index 0000000000..f8929117c1 --- /dev/null +++ b/docs/spec/proximity.en-US.md @@ -0,0 +1,37 @@ +--- +category: 十大原则 +order: 1 +title: Proximity +--- + +When several items are in close proximity to each other, they become one visual unit rather than several separate units. Otherwise, their distance should be larger and look more like several visual units. The basic purpose of proximity is to organize. To give an apparent view of the page structure and the hierarchy of information to users. + +--- + +## The relation of vertical spacing + +Example of the different vertical distance + +Divide the hierarchy of information through three formats:『small spacing』, 『middle spacing』and『large spacing』 + +
    + +Example of added element + +In the case that the three formats are not inapplicable, the hierarchy of information can be separated clearly through adding or cutting down the multiple of 『basic spacing』, or adding elements. + +> Note: in Ant Design, y=8+8*n, among which,n>=0,y stands for the vertical spacing and 8 represents 『basic spacing』. + +--- + +## Relationship of horizontal spacing + +Example of combination and configuration + +To adapt to screens of different sizes, in the horizontal direction, use grid layout to arrange the components to ensure the flexibility of the layout. + +
    + +Example of checkbox + +In the inner of a component, the horizontal spacing of elements should differ too. diff --git a/docs/spec/proximity.zh-CN.md b/docs/spec/proximity.zh-CN.md new file mode 100644 index 0000000000..d45747b53b --- /dev/null +++ b/docs/spec/proximity.zh-CN.md @@ -0,0 +1,38 @@ +--- +category: 十大原则 +order: 1 +subtitle: Proximity +english: 亲密性 +--- + +如果信息之间关联性越高,它们之间的距离就应该越接近,也越像一个视觉单元;反之,则它们的距离就应该越远,也越像多个视觉单元。亲密性的根本目的是实现组织性,让用户对页面结构和信息层次一目了然。 + +--- + +## 纵向间距关系 + +纵向间距示例 + +通过『小号间距』、『中号间距』、『大号间距』这三种规格来划分信息层次。 + +
    + +增加元素示例 + +在这三种规格不适用的情况下,可以通过加减『基础间距』的倍数,或者增加元素来拉开信息层次。 + +> 注:在 Ant Design 中,`y=8+8*n`。其中,`n>=0`,y 是纵向间距,8 是『基础间距』。 + +--- + +## 横向间距关系 + +组合排布示例 + +为了适用不同尺寸的屏幕,在横向采用栅格布局来排布组件,从而保证布局的灵活性。 + +
    + +复选框内示例 + +在一个组件内部,元素的横向间距也应该有所不同。 diff --git a/docs/spec/reaction.en-US.md b/docs/spec/reaction.en-US.md new file mode 100644 index 0000000000..b23d81c9e8 --- /dev/null +++ b/docs/spec/reaction.en-US.md @@ -0,0 +1,75 @@ +--- +category: 十大原则 +order: 10 +title: React Immediately +--- + +Invitations are powerful because they directly address discoverability and provide feedback before an interaction happens. Transitions are useful because they provide visual feedback during an interaction. But another class of feedback exists. It is the feedback that happens immediately after each interaction with the system, an immediate reaction paired with the user’s action. + +While we can’t literally extend Newton’s law to the world of user interfaces, we certainly can apply this principle to the way we should interact with users. When users click on a button, they expect the button to depress. When they type in a field, they expect to see characters show up in the text box. When they make a mistake, they want the application to tell them where they goofed. + +While there is a possibility of too much feedback (or, more accurately, too much of the wrong feedback—a concept we will discuss in the upcoming chapters), a system with little or no feedback feels sluggish and thickheaded. + +> ** Newton’s Third Law of Motion **: For every action, there is an equal and opposite reaction, from Wikipedia. + +--- + +## Lookup Patterns + + +example of Certain Category + +example of Uncertain Category + +Auto Complete: As the user types input into a field, a drop-down menu of matching values is displayed. +Depending on the categories of search results, it can be divided into two types, Certain Category and Uncertain Category. + +
    + +example of Live Search + +Live Suggest: Live Suggest provides real-time search term suggestions for creating a search. + +
    + +Refining Search: Refining Search provides a set of live filters that allow the search results to be tuned in real time. Learn more on [Pattern/Advanced Search](/docs/pattern/advanced-search). + +
    + +--- +## Live Suggest + +example of Live Preview + +Live Preview: A Live Preview gives the users a glimpse beforehand of how the application will interpret their input once submitted. + +>Note: An ounce of prevention is worth a pound of cure. Use Live Previews to prevent errors. + +
    + +Progressive Disclosure: When users are faced with a series of steps, it is often best to provide hints only when they are needed, instead of cluttering the interface by displaying all the hints at once. Learn more cases on [Stay on the Page/Progressive Disclosure](/docs/spec/stay#Process-Flows)。 + +
    + +example of Loading Button + +example of Loading Table + +example of Loading List + +example of Loading Page + + +Progress Indicator: Progress Indicators keep a conversation going with the user when the rest of the interface is currently unavailable. Common Progress Indicators, such as Loading Button, Loading Table, Loading List and Loading Page, can be displayed repectively according to the frequency and importance of operaction. + +
    + +example of Click Refresh + +Click Refresh: Click Refresh notifies the user of fresh content and provides button or tool to refresh. + +
    + +example of Periodic Refresh + +Periodic Refresh: Periodic Refresh brings in fresh content on a periodic basis without direct user interaction. diff --git a/docs/spec/reaction.zh-CN.md b/docs/spec/reaction.zh-CN.md new file mode 100644 index 0000000000..6fb73db4a9 --- /dev/null +++ b/docs/spec/reaction.zh-CN.md @@ -0,0 +1,77 @@ +--- +category: 十大原则 +order: 10 +subtitle: React Immediately +english: 即时反应 +--- + +『提供邀请』的强大体现在`交互之前`给出反馈,解决易发现性问题;『巧用过渡』的有用体现在它能够在`交互期间`为用户提供视觉反馈;『即时反应』的重要性体现在`交互之后`立即给出反馈。 + +就像『牛顿第三定律』所描述作用力和反作用一样,用户进行了操作或者内部数据发生了变化,系统就应该立即有一个对应的反馈,同时输入量级越大、重要性越高,那么反馈量级越大、重要性越高。 + +虽然反馈太多(准确的说,错误的反馈太多)是一个问题,但是反馈太少甚至没有反馈的系统,则让人感觉迟钝和笨拙,用户体验更差。 + +> ** 牛顿第三定律 ** :当两个物体互相作用时,彼此施加于对方的力,其大小相等、方向相反。——摘自《维基百科》 + +--- + +## 查询模式 + + +确定类目示例 + +不确定类目示例 + +自动完成:用户输入时,下拉列表会随着输入的关键词显示匹配项。 +根据查询结果分类的多少,可以分为『确定类目』、『不确定类目』两种类型。 + +
    + +实时搜索示例 + +实时搜索:随着用户输入,实时显示搜索结果。『自动完成』、『实时建议』的近亲。 + +
    + +微调搜索:随着用户调整搜索条件,实时调整搜索结构。具体可见:[『模式/高级搜索』](/docs/pattern/advanced-search)。 + +
    + +--- +## 反馈模式 + +实时预览示例 + +实时预览:在用户提交输入之前,让他先行了解系统将如何处理他的输入。 + +>注:解决错误最好的办法,就是不让错误发生。而『实时预览』就是有效避免错误的好设计。 + +
    + +渐进式展现:在必要的时候提供必要的提示,而不是一股脑儿显示所有提示,导致界面混乱,增加认知负担。案例详见[『足不出户/渐进式展现』](/docs/spec/stay#流程处理)。 + +
    + +按钮加载示例 + +表格加载示例 + +富列表加载示例 + +页面加载示例 + + +进度指示:当一个操作需要一定时间完成时,就需要即时告知进度,保持与用户的沟通。 +常见的进度指示:『按钮加载』、『表格加载』、『富列表加载』、『页面加载』。可根据操作的量级和重要性,展示不同类型的进度指示。 + +
    + +点击刷新示例 + +点击刷新:告知用户有新内容,并提供按钮等工具帮助用户查看新内容。 + +
    + +定时示例 + +定时刷新:无需用户介入,定时展示新内容。 diff --git a/docs/spec/repetition.en-US.md b/docs/spec/repetition.en-US.md new file mode 100644 index 0000000000..56283f09cb --- /dev/null +++ b/docs/spec/repetition.en-US.md @@ -0,0 +1,19 @@ +--- +category: 十大原则 +order: 4 +title: Repetition +--- + +The same elements keep repeating in the whole interface, which not only could lower the user’s learning cost effectively, but also help user recognize the relevance between these elements. + +--- + +## Repetitive elements + +Example of repetitive wireframe + +Example of repetitive design elements + +Example of repetitive of formats + +The repetitive element may be a thick rule(line), a wireframe, color, design elements, particular format, spatial relationships, etc. diff --git a/docs/spec/repetition.zh-CN.md b/docs/spec/repetition.zh-CN.md new file mode 100644 index 0000000000..92d9f40bcc --- /dev/null +++ b/docs/spec/repetition.zh-CN.md @@ -0,0 +1,20 @@ +--- +category: 十大原则 +order: 4 +subtitle: Repetition +english: 重复 +--- + +相同的元素在整个界面中不断重复,不仅可以有效降低用户的学习成本,也可以帮助用户识别出这些元素之间的关联性。 + +--- + +## 重复元素 + +线框重复示例 + +设计要素重复示例 + +文案格式重复示例 + +重复元素可以是一条粗线、一种线框,某种相同的颜色、设计要素、设计风格,某种格式、空间关系等。 diff --git a/docs/spec/stay.en-US.md b/docs/spec/stay.en-US.md new file mode 100644 index 0000000000..be8105a2bb --- /dev/null +++ b/docs/spec/stay.en-US.md @@ -0,0 +1,102 @@ +--- +category: 十大原则 +order: 6 +title: Stay on the Page +--- + +Solve most of problems on the same page and avoid a new one, because the page refresh and forwarding can lead to change blindness, in addition to disrupting the user’s mental flow. + +> ** Change Blindness** is a surprising perceptual phenomenon that occurs when a change in a visual stimulus is introduced and the observer does not notice it. People's poor ability to detect changes has been argued to reflect fundamental limitations of human attention,from the term of Change blindness, Wikipedia. + +> ** Flow**, also known as the zone, is the mental state of operation in which a person performing an activity is fully immersed in a feeling of energized focus, full involvement, and enjoyment in the process of the activity, from the term of Flow, Wikipedia + +--- + +## Overlays + +good example + +good example (special case) + +bad example + +Double-confirm overlay: Using the Modal to double confirm should be avoided, while affording an opportunity to undo is preferred. + +
    + +example of Detail Overlay + +Detail Overlay: Allows an overlay to present additional information when the user clicks or hovers over a link or section of content. + + +> Note that when a mouseover event occurs to trigger the Detail Overlay, 0.5-second delay needs to be added, and when the mouse is out, the overlay needs to be closed immediately. + +
    + +example of Input Overlay + +Input Overlay: Let the user enter small amounts of text on the overlay. + +
    + +--- + +## Inlays + +example of List Inlay + +List Inlay: Works as an effective way to hide detail until needed — while at the same time preserving space on the page for high-level overview information. + +
    + +

    Detail Inlay (coming soon)

    + +
    + +example of Tabs + +Tabs: Provides additional panels of information accessible by tab controls. + +
    + +--- + +## Virtual Pages + +In the process of interaction design, Overlays allow you to bring additional interactions or content in a layer above the cur- rent page. Inlays allow you to do this within the page itself. However, another powerful approach to keeping users engaged on the current page is to create a virtual page. That is to say, we create the illusion of a larger virtual page. + +Virtual Scrolling and Pagination, more on [Patterns/Lists/Show Long Lists](../pattern/list#显示长列表) + +Carousel, more on [Patterns/Lists/Show Images](../pattern/list#显示图片) + +
    + +

    Flexible User Interface (coming soon)

    + +
    + +--- + +## Process Flows + +It has long been common practice on the Web to turn each step into a sepa- rate page. While this may be the simplest way break down the problem, it may not lead to the best solution. For some Process Flows it makes sense to keep the user on the same page throughout the process. + +
    + +example of Responsive Disclosure + +Responsive Disclosure: Make the experience for selecting painless by providing disclosures as quickly as possible, and doing it all in a single-page interface. + +
    + +example of Configurator Process + +Configurator Process: Provides a configurator that allows users to help them accomplish the task or build their own product. + +
    + +example of Dialog Overlay Process + +Dialog Overlay Process: Any page switch is an interruption to the user’s mental flow. In addition, any context switch is a chance for a user to leave the site. But sometimes the step-by-step flow is necessary. + diff --git a/docs/spec/stay.zh-CN.md b/docs/spec/stay.zh-CN.md new file mode 100644 index 0000000000..ba9d4a2b15 --- /dev/null +++ b/docs/spec/stay.zh-CN.md @@ -0,0 +1,100 @@ +--- +category: 十大原则 +order: 6 +subtitle: Stay on the Page +english: 足不出户 +--- + +能在这个页面解决的问题,就不要去其它页面解决,因为任何页面刷新和跳转都会引起变化盲视(Change Blindness),导致用户心流(Flow)被打断。频繁的页面刷新和跳转,就像在看戏时,演员说完一行台词就安排一次谢幕一样。 + +> ** 变换盲视(Change Blindness)** :指视觉场景中的某些变化并未被观察者注意到的心理现象。产生这种现象的原因包括场景中的障碍物,眼球运动、地点的变化,或者是缺乏注意力等。——摘自《维基百科》 + +> ** 心流(Flow)** :也有别名以化境 (Zone) 表示,亦有人翻译为神驰状态,定义是一种将个人精神力完全投注在某种活动上的感觉;心流产生时同时会有高度的兴奋及充实感。——摘自《维基百科》 + +--- + +## 覆盖层 + +推荐示例 + +推荐示例 + +不推荐示例 + +二次确认覆盖层:避免滥用 Modal 进行二次确认,应该勇敢的让用户去尝试,给用户机会『撤消』即可。 + +
    + +详情覆盖层示例 + +详情覆盖层:一般在列表中,通过用户『悬停』/『点击』某个区块,在当前页加载该条列表项的更多详情信息。 + +> 注:使用『悬停』激活时,当鼠标移入时,需要设置 0.5 秒左右的延迟触发;当鼠标移出时,立刻关闭覆盖层 + +
    + +输入覆盖层示例 + +输入覆盖层:在覆盖层上,让用户直接进行少量字段的输入。 + +
    + +--- + +## 嵌入层 + +列表嵌入层示例 + +列表嵌入层:在列表中,显示某条列表项的详情信息,保持上下文不中断。 + +
    + +

    详情嵌入层 (敬请期待)

    + +
    + +标签页示例 + +标签页:将多个平级的信息进行整理和分类了,一次只显示一组信息。 + +
    + +--- + +## 虚拟页面 + +在交互过程中,『覆盖层』可以在当前页面上方显示附加内容和交互链接;『嵌入层』可以在页面内部实现同样效果;而『虚拟页面』不局限机械时代的『页面』,可以利用信息时代的特点构建一种新型『页面』。 + +无限加载和分页器,详见[『模式/列表/显示长列表』](../pattern/list#显示长列表) + +走马灯,详见[『模式/列表/显示图片』](../pattern/list#显示图片) + +
    + +

    伸缩式用户界面(敬请期待)

    + +
    + +--- + +## 流程处理 + +长期以来,Web 实现流程的方式就是把每个步骤放在一个单独的页面上。虽然这种做法是分解问题最简单的方式,但并不是最佳解决方案。对于某些『流程处理』而言,让用户始终待在同一个页面上则更有必要。 + +
    + +渐进式展现示例 + +渐进式展现:基于用户的操作/某种特定规则,渐进式展现不同的操作选项。 + +
    + +配置程序示例 + +配置程序:通过提供一系列的配置项,帮助用户完成任务或者产品组装。 + +
    + +弹出框覆盖层示例 + +弹出框覆盖层:虽然弹出框的出现会打断用户的心流,但是有时候在弹出框中使用『步骤条』来管理复杂流程也是可行的。 diff --git a/docs/spec/transition.en-US.md b/docs/spec/transition.en-US.md new file mode 100644 index 0000000000..dc7f6259ac --- /dev/null +++ b/docs/spec/transition.en-US.md @@ -0,0 +1,80 @@ +--- +category: 十大原则 +order: 9 +title: Use Transition +--- + +Our Gray Matter are wired to react to dynamic things like movement,shape change and colour change. Transitions smooth out the jarring world of the Web, making changes appear more natural. The main purpose for Transitions is to provide an engaging interface and reinforce communication. + +- Adding: The added elements should inform the users how to use, and the modified elements should be recognized. +- Receding: The irrelevant page elements should be removed properly. +- Normal: The elements without any change on the page can be safely ignored. + +--- + +## Maintain Context While Changing Views + +