diff --git a/build/config.js b/build/config.js index fac7ac199..09321ec8e 100644 --- a/build/config.js +++ b/build/config.js @@ -1,5 +1,5 @@ module.exports = { dev: { - componentName: 'timeline', // dev components + componentName: 'time-picker', // dev components }, }; diff --git a/components/time-picker/__tests__/__snapshots__/demo.test.js.snap b/components/time-picker/__tests__/__snapshots__/demo.test.js.snap index 0dba5a7f7..d705a27fd 100644 --- a/components/time-picker/__tests__/__snapshots__/demo.test.js.snap +++ b/components/time-picker/__tests__/__snapshots__/demo.test.js.snap @@ -6,11 +6,7 @@ exports[`renders ./components/time-picker/demo/addon.md correctly 1`] = `
`; -exports[`renders ./components/time-picker/demo/disabled.md correctly 1`] = ` - -`; +exports[`renders ./components/time-picker/demo/disabled.md correctly 1`] = ``; exports[`renders ./components/time-picker/demo/hide-column.md correctly 1`] = `
- - - - - - - - - + + + + + + + + + + + diff --git a/components/time-picker/index.en-US.md b/components/time-picker/index.en-US.md index eefc5ba4d..6174096c9 100644 --- a/components/time-picker/index.en-US.md +++ b/components/time-picker/index.en-US.md @@ -1,31 +1,32 @@ ## API -| Property | Description | Type | Default | -| --- | --- | --- | --- | -| addon | some addon to timepicker panel bottom | slot \| slot-scope | - | -| allowClear | allow clearing text | boolean | true | -| autoFocus | get focus when component mounted | boolean | false | -| clearText | clear tooltip of icon | string | clear | -| defaultOpenValue | default open panel value, used to set utcOffset,locale if value/defaultValue absent | [moment](http://momentjs.com/) | moment() | -| defaultValue | to set default time | [moment](http://momentjs.com/) | - | -| disabled | determine whether the TimePicker is disabled | boolean | false | -| disabledHours | to specify the hours that cannot be selected | function() | - | -| disabledMinutes | to specify the minutes that cannot be selected | function(selectedHour) | - | -| disabledSeconds | to specify the seconds that cannot be selected | function(selectedHour, selectedMinute) | - | -| format | to set the time format | string | "HH:mm:ss" | -| getPopupContainer | to set the container of the floating layer, while the default is to create a div element in body | function(trigger) | - | -| hideDisabledOptions | hide the options that can not be selected | boolean | false | -| hourStep | interval between hours in picker | number | 1 | -| inputReadOnly | Set the `readonly` attribute of the input tag (avoids virtual keyboard on touch devices) | boolean | false | -| minuteStep | interval between minutes in picker | number | 1 | -| open(.sync) | whether to popup panel | boolean | false | -| placeholder | display when there's no value | string | "Select a time" | -| popupClassName | className of panel | string | '' | -| popupStyle | style of panel | object | - | -| secondStep | interval between seconds in picker | number | 1 | -| suffixIcon | The custom suffix icon | string \| VNode \| slot | - | -| use12Hours | display as 12 hours format, with default format `h:mm:ss a` | boolean | false | -| value(v-model) | to set time | [moment](http://momentjs.com/) | - | +| Property | Description | Type | Default | Version | +| --- | --- | --- | --- | --- | +| addon | some addon to timepicker panel bottom | slot \| slot-scope | - | | +| allowClear | allow clearing text | boolean | true | | +| autoFocus | get focus when component mounted | boolean | false | | +| clearText | clear tooltip of icon | string | clear | | +| defaultOpenValue | default open panel value, used to set utcOffset,locale if value/defaultValue absent | [moment](http://momentjs.com/) | moment() | | +| defaultValue | to set default time | [moment](http://momentjs.com/) | - | | +| disabled | determine whether the TimePicker is disabled | boolean | false | | +| disabledHours | to specify the hours that cannot be selected | function() | - | | +| disabledMinutes | to specify the minutes that cannot be selected | function(selectedHour) | - | | +| disabledSeconds | to specify the seconds that cannot be selected | function(selectedHour, selectedMinute) | - | | +| format | to set the time format | string | "HH:mm:ss" | | +| getPopupContainer | to set the container of the floating layer, while the default is to create a div element in body | function(trigger) | - | | +| hideDisabledOptions | hide the options that can not be selected | boolean | false | | +| hourStep | interval between hours in picker | number | 1 | | +| inputReadOnly | Set the `readonly` attribute of the input tag (avoids virtual keyboard on touch devices) | boolean | false | | +| minuteStep | interval between minutes in picker | number | 1 | | +| open(.sync) | whether to popup panel | boolean | false | | +| placeholder | display when there's no value | string | "Select a time" | | +| popupClassName | className of panel | string | '' | | +| popupStyle | style of panel | object | - | | +| secondStep | interval between seconds in picker | number | 1 | | +| suffixIcon | The custom suffix icon | string \| VNode \| slot | - | | +| clearIcon | The custom clear icon | string \| VNode \| slot | - | 1.5.0 | +| use12Hours | display as 12 hours format, with default format `h:mm:ss a` | boolean | false | | +| value(v-model) | to set time | [moment](http://momentjs.com/) | - | | ### events diff --git a/components/time-picker/index.jsx b/components/time-picker/index.jsx index e29ff757f..8cb409249 100644 --- a/components/time-picker/index.jsx +++ b/components/time-picker/index.jsx @@ -2,11 +2,11 @@ import * as moment from 'moment'; import omit from 'omit.js'; import VcTimePicker from '../vc-time-picker'; import LocaleReceiver from '../locale-provider/LocaleReceiver'; -import defaultLocale from './locale/en_US'; import BaseMixin from '../_util/BaseMixin'; import PropTypes from '../_util/vue-types'; import warning from '../_util/warning'; import Icon from '../icon'; +import enUS from './locale/en_US'; import interopDefault from '../_util/interopDefault'; import { initDefaultProps, @@ -70,6 +70,8 @@ export const TimePickerProps = () => ({ transitionName: PropTypes.string, autoFocus: PropTypes.bool, addon: PropTypes.any, + clearIcon: PropTypes.any, + locale: PropTypes.object, }); const TimePicker = { @@ -87,6 +89,7 @@ const TimePicker = { placement: 'bottomLeft', transitionName: 'slide-up', focusOnOpen: true, + allowClear: true, }), model: { prop: 'value', @@ -107,7 +110,8 @@ const TimePicker = { } warning( !hasProp(this, 'allowEmpty'), - '`allowEmpty` in TimePicker is deprecated. Please use `allowClear` instead.', + 'TimePicker', + '`allowEmpty` is deprecated. Please use `allowClear` instead.', ); return { sValue: value, @@ -119,6 +123,30 @@ const TimePicker = { }, }, methods: { + getDefaultFormat() { + const { format, use12Hours } = this; + if (format) { + return format; + } else if (use12Hours) { + return 'h:mm:ss a'; + } + return 'HH:mm:ss'; + }, + + getAllowClear() { + const { allowClear, allowEmpty } = this.$props; + if (hasProp(this, 'allowClear')) { + return allowClear; + } + return allowEmpty; + }, + getDefaultLocale() { + const defaultLocale = { + ...enUS, + ...this.$props.locale, + }; + return defaultLocale; + }, savePopupRef(ref) { this.popupRef = ref; }, @@ -143,42 +171,29 @@ const TimePicker = { this.$refs.timePicker.blur(); }, - getDefaultFormat() { - const { format, use12Hours } = this; - if (format) { - return format; - } else if (use12Hours) { - return 'h:mm:ss a'; - } - return 'HH:mm:ss'; - }, - - getAllowClear() { - const { allowClear, allowEmpty } = this.$props; - if (hasProp(this, 'allowClear')) { - return allowClear; - } - return allowEmpty; - }, - renderInputIcon(prefixCls) { let suffixIcon = getComponentFromProp(this, 'suffixIcon'); suffixIcon = Array.isArray(suffixIcon) ? suffixIcon[0] : suffixIcon; const clockIcon = (suffixIcon && - (isValidElement(suffixIcon) ? ( - cloneElement(suffixIcon, { - class: `${prefixCls}-clock-icon`, - }) - ) : ( - {suffixIcon} - ))) || ; + isValidElement(suffixIcon) && + cloneElement(suffixIcon, { + class: `${prefixCls}-clock-icon`, + })) || ; return {clockIcon}; }, renderClearIcon(prefixCls) { - const clearIcon = ; - return clearIcon; + const clearIcon = getComponentFromProp(this, 'clearIcon'); + const clearIconPrefixCls = `${prefixCls}-clear`; + + if (clearIcon && isValidElement(clearIcon)) { + return cloneElement(clearIcon, { + class: clearIconPrefixCls, + }); + } + + return ; }, renderTimePicker(locale) { @@ -235,7 +250,7 @@ const TimePicker = { return ( ); diff --git a/components/time-picker/index.zh-CN.md b/components/time-picker/index.zh-CN.md index a00ea1710..efd14c063 100644 --- a/components/time-picker/index.zh-CN.md +++ b/components/time-picker/index.zh-CN.md @@ -1,31 +1,32 @@ ## API -| 参数 | 说明 | 类型 | 默认值 | -| --- | --- | --- | --- | -| addon | 选择框底部显示自定义的内容 | slot \| slot-scope | 无 | -| allowClear | 是否展示清除按钮 | boolean | true | -| autoFocus | 自动获取焦点 | boolean | false | -| clearText | 清除按钮的提示文案 | string | clear | -| defaultOpenValue | 当 defaultValue/value 不存在时,可以设置面板打开时默认选中的值 | [moment](http://momentjs.com/) | moment() | -| defaultValue | 默认时间 | [moment](http://momentjs.com/) | 无 | -| disabled | 禁用全部操作 | boolean | false | -| disabledHours | 禁止选择部分小时选项 | function() | 无 | -| disabledMinutes | 禁止选择部分分钟选项 | function(selectedHour) | 无 | -| disabledSeconds | 禁止选择部分秒选项 | function(selectedHour, selectedMinute) | 无 | -| format | 展示的时间格式 | string | "HH:mm:ss" | -| getPopupContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | 无 | -| hideDisabledOptions | 隐藏禁止选择的选项 | boolean | false | -| hourStep | 小时选项间隔 | number | 1 | -| inputReadOnly | 设置输入框为只读(避免在移动设备上打开虚拟键盘) | boolean | false | -| minuteStep | 分钟选项间隔 | number | 1 | -| open(.sync) | 面板是否打开 | boolean | false | -| placeholder | 没有值的时候显示的内容 | string | "请选择时间" | -| popupClassName | 弹出层类名 | string | '' | -| popupStyle | 弹出层样式对象 | object | - | -| secondStep | 秒选项间隔 | number | 1 | -| suffixIcon | 自定义的选择框后缀图标 | string \| VNode \| slot | - | -| use12Hours | 使用 12 小时制,为 true 时 `format` 默认为 `h:mm:ss a` | boolean | false | -| value(v-model) | 当前时间 | [moment](http://momentjs.com/) | 无 | +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| --- | --- | --- | --- | --- | +| addon | 选择框底部显示自定义的内容 | slot \| slot-scope | 无 | | +| allowClear | 是否展示清除按钮 | boolean | true | | +| autoFocus | 自动获取焦点 | boolean | false | | +| clearText | 清除按钮的提示文案 | string | clear | | +| defaultOpenValue | 当 defaultValue/value 不存在时,可以设置面板打开时默认选中的值 | [moment](http://momentjs.com/) | moment() | | +| defaultValue | 默认时间 | [moment](http://momentjs.com/) | 无 | | +| disabled | 禁用全部操作 | boolean | false | | +| disabledHours | 禁止选择部分小时选项 | function() | 无 | | +| disabledMinutes | 禁止选择部分分钟选项 | function(selectedHour) | 无 | | +| disabledSeconds | 禁止选择部分秒选项 | function(selectedHour, selectedMinute) | 无 | | +| format | 展示的时间格式 | string | "HH:mm:ss" | | +| getPopupContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | 无 | | +| hideDisabledOptions | 隐藏禁止选择的选项 | boolean | false | | +| hourStep | 小时选项间隔 | number | 1 | | +| inputReadOnly | 设置输入框为只读(避免在移动设备上打开虚拟键盘) | boolean | false | | +| minuteStep | 分钟选项间隔 | number | 1 | | +| open(.sync) | 面板是否打开 | boolean | false | | +| placeholder | 没有值的时候显示的内容 | string | "请选择时间" | | +| popupClassName | 弹出层类名 | string | '' | | +| popupStyle | 弹出层样式对象 | object | - | | +| secondStep | 秒选项间隔 | number | 1 | | +| suffixIcon | 自定义的选择框后缀图标 | string \| VNode \| slot | - | | +| clearIcon | 自定义的清除图标 | string \| VNode \| slot | - | 1.5.0 | +| use12Hours | 使用 12 小时制,为 true 时 `format` 默认为 `h:mm:ss a` | boolean | false | | +| value(v-model) | 当前时间 | [moment](http://momentjs.com/) | 无 | | ### 事件 diff --git a/components/vc-time-picker/Combobox.jsx b/components/vc-time-picker/Combobox.jsx index ae2c6e864..436ef894d 100644 --- a/components/vc-time-picker/Combobox.jsx +++ b/components/vc-time-picker/Combobox.jsx @@ -82,6 +82,9 @@ const Combobox = { onEnterSelectPanel(range) { this.__emit('currentSelectPanelChange', range); }, + onEsc(e) { + this.__emit('esc', e); + }, getHourSelect(hour) { const { prefixCls, hourOptions, disabledHours, showHour, use12Hours } = this; @@ -107,6 +110,7 @@ const Combobox = { type="hour" onSelect={this.onItemChange} onMouseenter={() => this.onEnterSelectPanel('hour')} + onEsc={this.onEsc} /> ); }, @@ -134,6 +138,7 @@ const Combobox = { type="minute" onSelect={this.onItemChange} onMouseenter={() => this.onEnterSelectPanel('minute')} + onEsc={this.onEsc} /> ); }, @@ -161,6 +166,7 @@ const Combobox = { type="second" onSelect={this.onItemChange} onMouseenter={() => this.onEnterSelectPanel('second')} + onEsc={this.onEsc} /> ); }, @@ -185,6 +191,7 @@ const Combobox = { type="ampm" onSelect={this.onItemChange} onMouseenter={() => this.onEnterSelectPanel('ampm')} + onEsc={this.onEsc} /> ); }, diff --git a/components/vc-time-picker/Header.jsx b/components/vc-time-picker/Header.jsx index 8432bccc3..04d53cb29 100644 --- a/components/vc-time-picker/Header.jsx +++ b/components/vc-time-picker/Header.jsx @@ -49,15 +49,13 @@ const Header = { } }, watch: { - $props: { - handler: function(nextProps) { - const { value, format } = nextProps; + value(val) { + this.$nextTick(() => { this.setState({ - str: (value && value.format(format)) || '', + str: (val && val.format(this.format)) || '', invalid: false, }); - }, - deep: true, + }); }, }, @@ -78,7 +76,6 @@ const Header = { disabledHours, disabledMinutes, disabledSeconds, - allowEmpty, value: originalValue, } = this; @@ -139,13 +136,8 @@ const Header = { } else if (originalValue !== value) { this.__emit('change', value); } - } else if (allowEmpty) { - this.__emit('change', null); } else { - this.setState({ - invalid: true, - }); - return; + this.__emit('change', null); } this.setState({ diff --git a/components/vc-time-picker/Panel.jsx b/components/vc-time-picker/Panel.jsx index cf0ee2e71..8f12cdff1 100644 --- a/components/vc-time-picker/Panel.jsx +++ b/components/vc-time-picker/Panel.jsx @@ -75,11 +75,9 @@ const Panel = { }, watch: { value(val) { - if (val) { - this.setState({ - sValue: val, - }); - } + this.setState({ + sValue: val, + }); }, }, @@ -101,7 +99,9 @@ const Panel = { close() { this.__emit('esc'); }, - + onEsc(e) { + this.__emit('esc', e); + }, disabledHours2() { const { use12Hours, disabledHours } = this; let disabledOptions = disabledHours(); @@ -191,7 +191,6 @@ const Panel = { disabledMinutes={disabledMinutes} disabledSeconds={disabledSeconds} onChange={this.onChange} - allowEmpty={allowEmpty} focusOnOpen={focusOnOpen} onKeydown={keydown} inputReadOnly={inputReadOnly} @@ -215,6 +214,7 @@ const Panel = { disabledSeconds={disabledSeconds} onCurrentSelectPanelChange={this.onCurrentSelectPanelChange} use12Hours={use12Hours} + onEsc={this.onEsc} isAM={this.isAM()} /> {addon(this)} diff --git a/components/vc-time-picker/Select.jsx b/components/vc-time-picker/Select.jsx index e9eb43b27..4cf15a920 100644 --- a/components/vc-time-picker/Select.jsx +++ b/components/vc-time-picker/Select.jsx @@ -1,6 +1,8 @@ import PropTypes from '../_util/vue-types'; import BaseMixin from '../_util/BaseMixin'; import classnames from 'classnames'; +import raf from 'raf'; + function noop() {} const scrollTo = (element, to, duration) => { const requestAnimationFrame = @@ -10,13 +12,15 @@ const scrollTo = (element, to, duration) => { }; // jump to target if duration zero if (duration <= 0) { - element.scrollTop = to; + raf(() => { + element.scrollTop = to; + }); return; } const difference = to - element.scrollTop; const perTick = (difference / duration) * 10; - requestAnimationFrame(() => { + raf(() => { element.scrollTop += perTick; if (element.scrollTop === to) return; scrollTo(element, to, duration - 10); @@ -58,7 +62,9 @@ const Select = { const { type } = this; this.__emit('select', type, value); }, - + onEsc(e) { + this.__emit('esc', e); + }, getOptions() { const { options, selectedIndex, prefixCls } = this; return options.map((item, index) => { @@ -71,8 +77,20 @@ const Select = { : () => { this.onSelect(item.value); }; + const onKeyDown = e => { + if (e.keyCode === 13) onClick(); + else if (e.keyCode === 27) this.onEsc(); + }; return ( -
  • +
  • {item.value}
  • ); diff --git a/components/vc-time-picker/TimePicker.jsx b/components/vc-time-picker/TimePicker.jsx index 10ca375b5..738c1da31 100644 --- a/components/vc-time-picker/TimePicker.jsx +++ b/components/vc-time-picker/TimePicker.jsx @@ -1,4 +1,5 @@ import moment from 'moment'; +import classNames from 'classnames'; import PropTypes from '../_util/vue-types'; import BaseMixin from '../_util/BaseMixin'; import { @@ -220,7 +221,6 @@ export default { showMinute={showMinute} showSecond={showSecond} onEsc={this.onEsc} - allowEmpty={allowEmpty} format={this.getFormat()} placeholder={placeholder} disabledHours={disabledHours} @@ -240,12 +240,8 @@ export default { }, getPopupClassName() { - const { showHour, showMinute, showSecond, use12Hours, prefixCls } = this; - let popupClassName = this.popupClassName; - // Keep it for old compatibility - if ((!showHour || !showMinute || !showSecond) && !use12Hours) { - popupClassName += ` ${prefixCls}-panel-narrow`; - } + const { showHour, showMinute, showSecond, use12Hours, prefixCls, popupClassName } = this; + let selectColumnCount = 0; if (showHour) { selectColumnCount += 1; @@ -259,8 +255,14 @@ export default { if (use12Hours) { selectColumnCount += 1; } - popupClassName += ` ${prefixCls}-panel-column-${selectColumnCount}`; - return popupClassName; + // Keep it for old compatibility + return classNames( + popupClassName, + { + [`${prefixCls}-panel-narrow`]: (!showHour || !showMinute || !showSecond) && !use12Hours, + }, + `${prefixCls}-panel-column-${selectColumnCount}`, + ); }, setOpen(open) { @@ -291,8 +293,8 @@ export default { }, renderClearButton() { const { sValue } = this; - const { prefixCls, allowEmpty, clearText } = this.$props; - if (!allowEmpty || !sValue) { + const { prefixCls, allowEmpty, clearText, disabled } = this.$props; + if (!allowEmpty || !sValue || disabled) { return null; } const clearIcon = getComponentFromProp(this, 'clearIcon'); diff --git a/components/vc-time-picker/index.js b/components/vc-time-picker/index.js index 7e491f7b5..bd9700cb4 100644 --- a/components/vc-time-picker/index.js +++ b/components/vc-time-picker/index.js @@ -1,2 +1,2 @@ -// based on rc-time-picker 3.6.2 +// based on rc-time-picker 3.7.3 export { default } from './TimePicker';