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; + } + } +}