diff --git a/components/datepicker/demo/time.md b/components/datepicker/demo/time.md
index af6f0196f5cefd24b54fd48d8a816fea23e4a1a6..d72c4de84420cfc74f8682f6587b1240eca2ff4a 100644
--- a/components/datepicker/demo/time.md
+++ b/components/datepicker/demo/time.md
@@ -13,4 +13,3 @@ ReactDOM.render(
, document.getElementById('components-datepicker-demo-time'));
````
-
diff --git a/components/datepicker/index.jsx b/components/datepicker/index.jsx
index d6f5547fe46227b591fd0c5668b89e901e840028..64099ffb1a64e050b67a8aefdd987d3377197cc5 100644
--- a/components/datepicker/index.jsx
+++ b/components/datepicker/index.jsx
@@ -13,7 +13,6 @@ function createPicker(TheCalendar) {
getDefaultProps() {
return {
format: 'yyyy-MM-dd',
- placeholder: '',
transitionName: 'slide-up',
popupStyle: {},
onSelect: null, // 向前兼容
@@ -94,7 +93,8 @@ function createPicker(TheCalendar) {
let defaultCalendarValue = new GregorianCalendar(this.getLocale());
defaultCalendarValue.setTime(Date.now());
- const placeholder = this.props.placeholder || this.getLocale().lang.placeholder;
+ const placeholder = ('placeholder' in this.props)
+ ? this.placeholder : this.getLocale().lang.placeholder;
const calendar = (
.code-box-demo .ant-calendar-picker {
- margin: 0 12px 12px 0;
+ margin: 0 8px 12px 0;
}
diff --git a/components/datepicker/locale/en_US.js b/components/datepicker/locale/en_US.js
index 657685915940fb5cde529e9c1d5d7033f020355e..cb67d925d146f3b07b8ccc75c078289bf8a2512f 100644
--- a/components/datepicker/locale/en_US.js
+++ b/components/datepicker/locale/en_US.js
@@ -5,7 +5,7 @@ import CalendarLocale from 'rc-calendar/lib/locale/en_US';
// 统一合并为完整的 Locale
let locale = objectAssign({}, GregorianCalendarLocale);
locale.lang = objectAssign({
- placeholder: 'Please select a date'
+ placeholder: 'Select a date'
}, CalendarLocale);
// All settings at:
diff --git a/components/timepicker/demo/basic.md b/components/timepicker/demo/basic.md
new file mode 100644
index 0000000000000000000000000000000000000000..b1b77d953e499cd3697a4d94a6cc99210ccb62f1
--- /dev/null
+++ b/components/timepicker/demo/basic.md
@@ -0,0 +1,21 @@
+# 基本
+
+- order: 0
+
+最简单的用法。
+
+---
+
+````jsx
+import { Timepicker } from 'antd';
+
+function onChange(time) {
+ console.log(time.toLocaleTimeString('zh-CN', {
+ hour12: false
+ }));
+}
+
+ReactDOM.render(
+
+, document.getElementById('components-timepicker-demo-basic'));
+````
diff --git a/components/timepicker/demo/disabled.md b/components/timepicker/demo/disabled.md
new file mode 100644
index 0000000000000000000000000000000000000000..dc348cb2bd30bc95880195ddba0b005069235901
--- /dev/null
+++ b/components/timepicker/demo/disabled.md
@@ -0,0 +1,15 @@
+# 禁用
+
+- order: 4
+
+禁用时间选择。
+
+---
+
+````jsx
+import { Timepicker } from 'antd';
+
+ReactDOM.render(
+
+, document.getElementById('components-timepicker-demo-disabled'));
+````
diff --git a/components/timepicker/demo/no-default-value.md b/components/timepicker/demo/no-default-value.md
new file mode 100644
index 0000000000000000000000000000000000000000..a539e8ae5ddab4ae812355db1acd677ebb2291fd
--- /dev/null
+++ b/components/timepicker/demo/no-default-value.md
@@ -0,0 +1,15 @@
+# 无默认值
+
+- order: 5
+
+没有默认时间。
+
+---
+
+````jsx
+import { Timepicker } from 'antd';
+
+ReactDOM.render(
+
+, document.getElementById('components-timepicker-demo-no-default-value'));
+````
diff --git a/components/timepicker/demo/size.md b/components/timepicker/demo/size.md
new file mode 100644
index 0000000000000000000000000000000000000000..051ad5323720181040ff16c23c4d4aaa5b4451fa
--- /dev/null
+++ b/components/timepicker/demo/size.md
@@ -0,0 +1,19 @@
+# 三种大小
+
+- order: 6
+
+三种大小的输入框,大的用在表单中,中的为默认。
+
+---
+
+````jsx
+import { Timepicker } from 'antd';
+
+ReactDOM.render(
+
+
+
+
+
+, document.getElementById('components-timepicker-demo-size'));
+````
diff --git a/components/timepicker/demo/special-minutes.md b/components/timepicker/demo/special-minutes.md
new file mode 100644
index 0000000000000000000000000000000000000000..42b76c259960a78e7c171cc9300e24f25e0ff5b2
--- /dev/null
+++ b/components/timepicker/demo/special-minutes.md
@@ -0,0 +1,15 @@
+# 特定选项
+
+- order: 3
+
+分钟只提供特定的选择,同时可以通过 `hourOptions` 和 `secondOptions` 对小时和秒进行特殊的限定。
+
+---
+
+````jsx
+import { Timepicker } from 'antd';
+
+ReactDOM.render(
+
+, document.getElementById('components-timepicker-demo-special-minutes'));
+````
diff --git a/components/timepicker/demo/without-seconds.md b/components/timepicker/demo/without-seconds.md
new file mode 100644
index 0000000000000000000000000000000000000000..7767e651e9e0fd52e58fc68039160265c022025c
--- /dev/null
+++ b/components/timepicker/demo/without-seconds.md
@@ -0,0 +1,15 @@
+# 不展示秒
+
+- order: 2
+
+不展示秒,也不允许选择。
+
+---
+
+````jsx
+import { Timepicker } from 'antd';
+
+ReactDOM.render(
+
+, document.getElementById('components-timepicker-demo-without-seconds'));
+````
diff --git a/components/timepicker/index.jsx b/components/timepicker/index.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..13e9779bb35027a3c21cae17dc21b623ad2d4250
--- /dev/null
+++ b/components/timepicker/index.jsx
@@ -0,0 +1,85 @@
+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';
+
+const AntTimepicker = React.createClass({
+ getDefaultProps() {
+ return {
+ format: 'HH:mm:ss',
+ prefixCls: 'ant-timepicker',
+ onChange() {},
+ locale: {},
+ align: {
+ offset: [0, -1],
+ },
+ open: false,
+ disabled: false,
+ hourOptions: undefined,
+ minuteOptions: undefined,
+ secondOptions: undefined,
+ size: '',
+ placement: 'bottomLeft',
+ transitionName: 'slide-up',
+ };
+ },
+
+ /**
+ * 获得输入框的 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;
+ },
+
+ /**
+ * 获得输入框的默认值
+ */
+ getDefaultValue(formatter) {
+ const defaultValue = this.props.defaultValue;
+ if (defaultValue) {
+ return formatter.parse(defaultValue, {
+ locale: this.getLocale(),
+ obeyCount: true,
+ });
+ }
+ return undefined;
+ },
+
+ handleChange(value) {
+ this.props.onChange(new Date(value.getTime()));
+ },
+
+ getLocale() {
+ // 统一合并为完整的 Locale
+ let locale = objectAssign({}, defaultLocale, this.props.locale);
+ locale.lang = objectAssign({}, defaultLocale.lang, this.props.locale.lang);
+ return locale;
+ },
+
+ render() {
+ const { format } = this.props;
+ const formatter = new DateTimeFormat(format);
+ const placeholder = ('placeholder' in this.props)
+ ? this.props.placeholder : this.getLocale().lang.placeholder;
+ return (
+
+ );
+ }
+
+});
+
+export default AntTimepicker;
diff --git a/components/timepicker/index.md b/components/timepicker/index.md
new file mode 100644
index 0000000000000000000000000000000000000000..a8e1d86eef7916999361e0c87c3f316151811770
--- /dev/null
+++ b/components/timepicker/index.md
@@ -0,0 +1,37 @@
+# Timepicker
+
+- category: Components
+- chinese: 时间选择框
+- type: 表单
+
+---
+
+输入或选择时间的控件。
+
+何时使用
+--------
+
+当用户需要输入一个时间,可以点击标准输入框,弹出时间面板进行选择。
+
+API
+---
+
+```html
+
+```
+
+| 参数 | 说明 | 类型 | 默认值 |
+|-----------------|-----|-----|-------|
+| defaultValue | 默认时间 | string | 无 |
+| placeholder | 没有值的时候显示的内容 | string | "请选择时间" |
+| onChange | 时间发生变化的回调 | function(Date value) | 无 |
+| format | 展示的时间格式 | string | "HH:mm:ss"、"HH:mm"、"mm:ss" |
+| disabled | 禁用 | bool | false |
+| hourOptions | 特定可选择的小时 | array | 0 到 24 组成的数组 |
+| minuteOptions | 特定可选择的分钟 | array | 0 到 60 组成的数组 |
+| secondOptions | 特定可选择的秒 | array | 0 到 60 组成的数组 |
+| inputClassName | 输入框的样式 | string | |
+| placement | 显示位置 | string | bottomLeft |
+| transitionName | 显示动画 | string | slide-up |
+
+
diff --git a/components/timepicker/locale/en_US.js b/components/timepicker/locale/en_US.js
new file mode 100644
index 0000000000000000000000000000000000000000..7abd8877d01866b866238af08fca72fd84cf5fc2
--- /dev/null
+++ b/components/timepicker/locale/en_US.js
@@ -0,0 +1,14 @@
+import objectAssign from 'object-assign';
+import GregorianCalendarLocale from 'gregorian-calendar/lib/locale/en_US';
+import TimepickerLocale from 'rc-time-picker/lib/locale/en_US';
+
+// 统一合并为完整的 Locale
+let locale = objectAssign({}, GregorianCalendarLocale);
+locale.lang = objectAssign({
+ placeholder: 'Select a time'
+}, TimepickerLocale);
+
+// All settings at:
+// https://github.com/ant-design/ant-design/issues/424
+
+export default locale;
diff --git a/components/timepicker/locale/zh_CN.js b/components/timepicker/locale/zh_CN.js
new file mode 100644
index 0000000000000000000000000000000000000000..4560232f2ab47c7b1dc2ccf1286b50e1e04d2f00
--- /dev/null
+++ b/components/timepicker/locale/zh_CN.js
@@ -0,0 +1,14 @@
+import objectAssign from 'object-assign';
+import GregorianCalendarLocale from 'gregorian-calendar/lib/locale/zh_CN';
+import TimepickerLocale from 'rc-time-picker/lib/locale/zh_CN';
+
+// 统一合并为完整的 Locale
+let locale = objectAssign({}, GregorianCalendarLocale);
+locale.lang = objectAssign({
+ placeholder: '请选择时间'
+}, TimepickerLocale);
+
+// All settings at:
+// https://github.com/ant-design/ant-design/issues/424
+
+export default locale;
diff --git a/index.js b/index.js
index 710600f886d44bcee15d238a5d77fabe566a2ee2..f2d2ee14763c512758f27dd08067c91c4f1965b0 100644
--- a/index.js
+++ b/index.js
@@ -59,6 +59,7 @@ const antd = {
Form: require('./components/form').Form,
Input: require('./components/form').Input,
Calendar: require('./components/calendar'),
+ Timepicker: require('./components/timepicker'),
};
antd.version = require('./package.json').version;
diff --git a/package.json b/package.json
index 7a9b17959ca4f089cd65cf7f280e7a1699e917d8..532504a30af5cf90d0e475d3633a5d56a931d77a 100644
--- a/package.json
+++ b/package.json
@@ -57,6 +57,7 @@
"rc-switch": "~1.3.1",
"rc-table": "~3.6.1",
"rc-tabs": "~5.5.0",
+ "rc-time-picker": "~0.5.6",
"rc-tooltip": "~3.2.0",
"rc-tree": "~0.19.0",
"rc-trigger": "~1.0.6",
diff --git a/style/components/datepicker/Picker.less b/style/components/datepicker/Picker.less
index a63b30f950569adeac2efa0e00551f8978e1c7e5..3e773d20154762ce37284e5cb372ba6a48e69c10 100644
--- a/style/components/datepicker/Picker.less
+++ b/style/components/datepicker/Picker.less
@@ -1,4 +1,4 @@
-.@{calendar-prefix-cls}-picker-container {
+.@{calendar-prefix-cls}-picker-container {
position: absolute;
z-index: 1070;
diff --git a/style/components/form.less b/style/components/form.less
index cbfce2ea76eebb9c7a835919295c61dfdef2b8b6..6122c29f37dac9e17e295409336e8cb245876eaa 100644
--- a/style/components/form.less
+++ b/style/components/form.less
@@ -324,6 +324,11 @@ form {
color: @warning-color;
}
+ // ant-timepicker
+ .@{timepicker-prefix-cls}-picker-icon:after {
+ color: @warning-color;
+ }
+
// ant-input-number
.@{input-number-prefix-cls} {
border-color: @warning-color;
@@ -359,6 +364,11 @@ form {
color: @error-color;
}
+ // ant-timepicker
+ .@{timepicker-prefix-cls}-picker-icon:after {
+ color: @error-color;
+ }
+
// ant-input-number
.@{input-number-prefix-cls} {
border-color: @error-color;
diff --git a/style/components/index.less b/style/components/index.less
index c2a292d9ac6a341767931765bcad4e48396c6a6d..5b0b8d25c541103b87f04500f647e013a8f323ad 100644
--- a/style/components/index.less
+++ b/style/components/index.less
@@ -4,6 +4,7 @@
@import "progress";
@import "select";
@import "datepicker";
+@import "timepicker";
@import "dialog";
@import "confirm";
@import "tabs";
@@ -36,3 +37,4 @@
@import "timeline";
@import "spin";
@import "calendar";
+@import "timepicker";
diff --git a/style/components/timepicker.less b/style/components/timepicker.less
new file mode 100644
index 0000000000000000000000000000000000000000..84a5821b5abbcf4f3025d52f4a6d760f9693c84e
--- /dev/null
+++ b/style/components/timepicker.less
@@ -0,0 +1,13 @@
+@timepicker-prefix-cls: ant-timepicker;
+
+.@{timepicker-prefix-cls} {
+ box-sizing: border-box;
+ * {
+ box-sizing: border-box;
+ }
+}
+
+@import "timepicker/Picker";
+@import "timepicker/Panel";
+@import "timepicker/Header";
+@import "timepicker/Select";
diff --git a/style/components/timepicker/Header.less b/style/components/timepicker/Header.less
new file mode 100644
index 0000000000000000000000000000000000000000..e9968c3de15f5b10dbed7236e01ce78a6f219d4f
--- /dev/null
+++ b/style/components/timepicker/Header.less
@@ -0,0 +1,50 @@
+.@{timepicker-prefix-cls} {
+ &-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 #e9e9e9;
+ }
+
+ &-invalid {
+ border-color: red;
+ }
+ }
+
+ &-clear-btn {
+ position: absolute;
+ right: 6px;
+ cursor: pointer;
+ overflow: hidden;
+ width: 20px;
+ height: 20px;
+ text-align: center;
+ line-height: 20px;
+ top: 6px;
+ margin: 0;
+ }
+
+ &-clear-btn:after {
+ content: "\e631";
+ font-family: "anticon";
+ font-size: 12px;
+ color: #aaa;
+ display: inline-block;
+ line-height: 1;
+ width: 20px;
+ transition: color 0.3s ease;
+ }
+
+ &-clear-btn:hover:after {
+ color: #999;
+ }
+}
diff --git a/style/components/timepicker/Panel.less b/style/components/timepicker/Panel.less
new file mode 100644
index 0000000000000000000000000000000000000000..fc6607ee3a1871310f25ea4e9d97d19eb1b7de3b
--- /dev/null
+++ b/style/components/timepicker/Panel.less
@@ -0,0 +1,15 @@
+.@{timepicker-prefix-cls}-panel {
+ display: inline-block;
+ position: relative;
+ outline: none;
+ border: 1px solid #ccc;
+ list-style: none;
+ font-size: 12px;
+ text-align: left;
+ background-color: #fff;
+ border-radius: @border-radius-base;
+ box-shadow: 0 1px 5px #ccc;
+ background-clip: padding-box;
+ border: 1px solid #ccc;
+ line-height: 1.5;
+}
diff --git a/style/components/timepicker/Picker.less b/style/components/timepicker/Picker.less
new file mode 100644
index 0000000000000000000000000000000000000000..a64abba94ce500e3f66f2d8969167c22d4a901b4
--- /dev/null
+++ b/style/components/timepicker/Picker.less
@@ -0,0 +1,66 @@
+.@{timepicker-prefix-cls}-picker-container {
+ z-index: 1070;
+ position: absolute;
+
+ &.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}-picker {
+ position: relative;
+ display: inline-block;
+ outline: none;
+ font-size: @font-size-base;
+ transition: opacity 0.3s ease;
+
+ > input {
+ width: 100px;
+ }
+
+ &-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/style/components/timepicker/Select.less b/style/components/timepicker/Select.less
new file mode 100644
index 0000000000000000000000000000000000000000..b7c8dd09918ee8ea9abbe59f86a8bf8cfefbcc9c
--- /dev/null
+++ b/style/components/timepicker/Select.less
@@ -0,0 +1,51 @@
+.@{timepicker-prefix-cls}-select {
+ float: left;
+ overflow-y:auto;
+ font-size: 12px;
+ border: 1px solid #e9e9e9;
+ border-width: 0 1px;
+ margin-left: -1px;
+ box-sizing: border-box;
+ width: 56px;
+
+ &: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: border-box;
+ margin: 0;
+ padding: 0 0 0 16px;
+ width: 100%;
+ height: 24px;
+ line-height: 24px;
+ text-align: left;
+ cursor: pointer;
+ user-select: none;
+
+ &.selected {
+ background: tint(@primary-color, 90%);
+ color: @link-color;
+ }
+
+ &:hover {
+ background: tint(@primary-color, 90%);
+ transition: background 0.3s ease;
+ }
+ }
+}