feat(date-picker): shortcuts function value (#1415)

* feat(date-picker): shortcuts function value

* feat(date-picker): Shortcuts remove data parameter

* Apply suggestions from code review

Co-authored-by: aaronz <aaron.bjym1011@outlook.com>
Co-authored-by: 07akioni <07akioni2@gmail.com>
This commit is contained in:
bjym 2021-10-27 01:31:50 +08:00 committed by GitHub
parent 0e8486dadf
commit f826003544
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 161 additions and 45 deletions

View File

@ -9,6 +9,10 @@
- Fix `n-log` `font-size` prop not working, closes [#1416](https://github.com/TuSimple/naive-ui/issues/1416). - Fix `n-log` `font-size` prop not working, closes [#1416](https://github.com/TuSimple/naive-ui/issues/1416).
- Fix `n-loading-bar` will show once even if `start` is not called when `loading-bar-style` is set. - Fix `n-loading-bar` will show once even if `start` is not called when `loading-bar-style` is set.
### Feats
- `n-date-picker`'s `shortcuts` prop supports functional value.
## 2.19.11 (2021-10-21) ## 2.19.11 (2021-10-21)
### Fixes ### Fixes

View File

@ -9,6 +9,10 @@
- 修复 `n-log` `font-size` 属性不生效,关闭 [#1416](https://github.com/TuSimple/naive-ui/issues/1416) - 修复 `n-log` `font-size` 属性不生效,关闭 [#1416](https://github.com/TuSimple/naive-ui/issues/1416)
- 修复 `n-loading-bar` 设定 `loading-bar-style` 后不调用 `start` 也会显示一次 - 修复 `n-loading-bar` 设定 `loading-bar-style` 后不调用 `start` 也会显示一次
### Feats
- `n-date-picker``shortcuts` 属性支持传入回调函数
## 2.19.11 (2021-10-21) ## 2.19.11 (2021-10-21)
### Fixes ### Fixes

View File

@ -32,7 +32,7 @@ update-on-close
| disabled | `boolean` | `false` | Whether the date picker is disabled. | | disabled | `boolean` | `false` | Whether the date picker is disabled. |
| first-day-of-week | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `undefined` | The first day of a week on calendar, 0 means Monday. | | first-day-of-week | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `undefined` | The first day of a week on calendar, 0 means Monday. |
| input-readonly | `boolean` | `false` | Set the `readonly` attribute of the input (avoids virtual keyboard on touch devices). | | input-readonly | `boolean` | `false` | Set the `readonly` attribute of the input (avoids virtual keyboard on touch devices). |
| shortcuts | `Record<string, number \| [number, number]>` | `undefined` | Shortcut button customizations. | | shortcuts | `Record<string, number \| (() => number)> \| Record<string, [number, number] \| (() => [number, number])>` | `undefined` | Shortcut button customizations. |
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | Date picker size. | | size | `'small' \| 'medium' \| 'large'` | `'medium'` | Date picker size. |
| type | `'date' \| 'datetime' \| 'daterange' \|'datetimerange' \|'month'` | `'date'` | Date picker type. | | type | `'date' \| 'datetime' \| 'daterange' \|'datetimerange' \|'month'` | `'date'` | Date picker type. |
| value | `number \| [number, number] \| null` | `undefined` | Value of the date picker when being manually set. | | value | `number \| [number, number] \| null` | `undefined` | Value of the date picker when being manually set. |

View File

@ -31,10 +31,14 @@ export default defineComponent({
range2: ref(null), range2: ref(null),
shortcuts: { shortcuts: {
'Honey birthday': 1631203200000, 'Honey birthday': 1631203200000,
'Party day': 1629216000000 Yesterday: () => new Date().getTime() - 24 * 60 * 60 * 1000
}, },
rangeShortcuts: { rangeShortcuts: {
'Happy holiday': [1629216000000, 1631203200000] 'Happy holiday': [1629216000000, 1631203200000],
'Last 2 hours': () => {
const cur = new Date().getTime()
return [cur - 2 * 60 * 60 * 1000, cur]
}
} }
} }
} }

View File

@ -32,7 +32,7 @@ update-on-close
| disabled | `boolean` | `false` | 是否禁用 | | disabled | `boolean` | `false` | 是否禁用 |
| first-day-of-week | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `undefined` | 日历上一周的开始0 代表周一 | | first-day-of-week | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `undefined` | 日历上一周的开始0 代表周一 |
| input-readonly | `boolean` | `false` | 设置输入框为只读(避免在移动设备上打开虚拟键盘) | | input-readonly | `boolean` | `false` | 设置输入框为只读(避免在移动设备上打开虚拟键盘) |
| shortcuts | `Record<string, number \| [number, number]>` | `undefined` | 自定义快捷按钮 | | shortcuts | `Record<string, number \| (() => number)> \| Record<string, [number, number] \| (() => [number, number])>` | `undefined` | 自定义快捷按钮 |
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | 尺寸 | | size | `'small' \| 'medium' \| 'large'` | `'medium'` | 尺寸 |
| type | `'date' \| 'datetime' \| 'daterange' \|'datetimerange' \|'month'` | `'date'` | Date Picker 的类型 | | type | `'date' \| 'datetime' \| 'daterange' \|'datetimerange' \|'month'` | `'date'` | Date Picker 的类型 |
| value | `number \| [number, number] \| null` | `undefined` | Date Picker 的值 | | value | `number \| [number, number] \| null` | `undefined` | Date Picker 的值 |

View File

@ -10,6 +10,7 @@
v-model:value="range1" v-model:value="range1"
type="daterange" type="daterange"
:shortcuts="rangeShortcuts" :shortcuts="rangeShortcuts"
:update-value-on-close="true"
/> />
<n-date-picker <n-date-picker
v-model:value="range2" v-model:value="range2"
@ -31,10 +32,15 @@ export default defineComponent({
range2: ref(null), range2: ref(null),
shortcuts: { shortcuts: {
亲爱的生日: 1631203200000, 亲爱的生日: 1631203200000,
派对日: 1629216000000 派对日: 1629216000000,
昨天: () => new Date().getTime() - 24 * 60 * 60 * 1000
}, },
rangeShortcuts: { rangeShortcuts: {
快乐假期: [1629216000000, 1631203200000] 快乐假期: [1629216000000, 1631203200000],
近2小时: () => {
const cur = new Date().getTime()
return [cur - 2 * 60 * 60 * 1000, cur]
}
} }
} }
} }

View File

@ -17,8 +17,8 @@ import {
export type Value = number | [number, number] export type Value = number | [number, number]
export type Shortcuts = export type Shortcuts =
| Record<string, number> | Record<string, number | (() => number)>
| Record<string, [number, number]> | Record<string, [number, number] | (() => [number, number])>
export type OnUpdateValue = ( export type OnUpdateValue = (
value: number & (number | null) & [number, number] & ([number, number] | null) value: number & (number | null) & [number, number] & ([number, number] | null)

View File

@ -127,16 +127,13 @@ export default defineComponent({
<NxButton <NxButton
size="tiny" size="tiny"
onMouseenter={() => { onMouseenter={() => {
this.cachePendingValue() this.handleSingleShortcutMouseenter(shortcut)
this.doUpdateValue(shortcut, false)
}} }}
onClick={() => { onClick={() => {
this.doUpdateValue(shortcut, false) this.handleSingleShortcutClick(shortcut)
this.clearPendingValue()
this.handleConfirmClick()
}} }}
onMouseleave={() => { onMouseleave={() => {
this.restorePendingValue() this.handleShortcutMouseleave()
}} }}
> >
{{ default: () => key }} {{ default: () => key }}

View File

@ -211,20 +211,18 @@ export default defineComponent({
{shortcuts && {shortcuts &&
Object.keys(shortcuts).map((key) => { Object.keys(shortcuts).map((key) => {
const shortcut = shortcuts[key] const shortcut = shortcuts[key]
return Array.isArray(shortcut) ? ( return Array.isArray(shortcut) ||
typeof shortcut === 'function' ? (
<NxButton <NxButton
size="tiny" size="tiny"
onMouseenter={() => { onMouseenter={() => {
this.cachePendingValue() this.handleRangeShortcutMouseenter(shortcut)
this.changeStartEndTime(...shortcut)
}} }}
onClick={() => { onClick={() => {
this.changeStartEndTime(...shortcut) this.handleRangeShortcutClick(shortcut)
this.clearPendingValue()
this.handleConfirmClick()
}} }}
onMouseleave={() => { onMouseleave={() => {
this.restorePendingValue() this.handleShortcutMouseleave()
}} }}
> >
{{ default: () => key }} {{ default: () => key }}

View File

@ -147,16 +147,13 @@ export default defineComponent({
<NxButton <NxButton
size="tiny" size="tiny"
onMouseenter={() => { onMouseenter={() => {
this.cachePendingValue() this.handleSingleShortcutMouseenter(shortcut)
this.doUpdateValue(shortcut, false)
}} }}
onClick={() => { onClick={() => {
this.doUpdateValue(shortcut, false) this.handleSingleShortcutClick(shortcut)
this.clearPendingValue()
this.handleConfirmClick()
}} }}
onMouseleave={() => { onMouseleave={() => {
this.restorePendingValue() this.handleShortcutMouseleave()
}} }}
> >
{{ default: () => key }} {{ default: () => key }}

View File

@ -269,20 +269,18 @@ export default defineComponent({
{shortcuts && {shortcuts &&
Object.keys(shortcuts).map((key) => { Object.keys(shortcuts).map((key) => {
const shortcut = shortcuts[key] const shortcut = shortcuts[key]
return Array.isArray(shortcut) ? ( return Array.isArray(shortcut) ||
typeof shortcut === 'function' ? (
<NxButton <NxButton
size="tiny" size="tiny"
onMouseenter={() => { onMouseenter={() => {
this.cachePendingValue() this.handleRangeShortcutMouseenter(shortcut)
this.changeStartEndTime(...shortcut)
}} }}
onClick={() => { onClick={() => {
this.changeStartEndTime(...shortcut) this.handleRangeShortcutClick(shortcut)
this.clearPendingValue()
this.handleConfirmClick()
}} }}
onMouseleave={() => { onMouseleave={() => {
this.restorePendingValue() this.handleShortcutMouseleave()
}} }}
> >
{{ default: () => key }} {{ default: () => key }}

View File

@ -135,16 +135,13 @@ export default defineComponent({
<NxButton <NxButton
size="tiny" size="tiny"
onMouseenter={() => { onMouseenter={() => {
this.cachePendingValue() this.handleSingleShortcutMouseenter(shortcut)
this.doUpdateValue(shortcut, false)
}} }}
onClick={() => { onClick={() => {
this.doUpdateValue(shortcut, false) this.handleSingleShortcutClick(shortcut)
this.clearPendingValue()
this.handleConfirmClick()
}} }}
onMouseleave={() => { onMouseleave={() => {
this.restorePendingValue() this.handleShortcutMouseleave()
}} }}
> >
{{ default: () => key }} {{ default: () => key }}

View File

@ -16,7 +16,11 @@ import {
} from 'date-fns' } from 'date-fns'
import { dateArray, monthArray, strictParse, yearArray } from '../utils' import { dateArray, monthArray, strictParse, yearArray } from '../utils'
import { usePanelCommon } from './use-panel-common' import { usePanelCommon } from './use-panel-common'
import { IsSingleDateDisabled, datePickerInjectionKey } from '../interface' import {
IsSingleDateDisabled,
datePickerInjectionKey,
Shortcuts
} from '../interface'
import type { DateItem, MonthItem, YearItem } from '../utils' import type { DateItem, MonthItem, YearItem } from '../utils'
import { VirtualListInst } from 'vueuc' import { VirtualListInst } from 'vueuc'
import { ScrollbarInst } from '../../../_internal' import { ScrollbarInst } from '../../../_internal'
@ -280,6 +284,19 @@ function useCalendar (
function handleTimePickerChange (value: number): void { function handleTimePickerChange (value: number): void {
panelCommon.doUpdateValue(value, false) panelCommon.doUpdateValue(value, false)
} }
function handleSingleShortcutMouseenter (shortcut: Shortcuts[string]): void {
panelCommon.cachePendingValue()
const shortcutValue = panelCommon.getShortcutValue(shortcut)
if (typeof shortcutValue !== 'number') return
panelCommon.doUpdateValue(shortcutValue, false)
}
function handleSingleShortcutClick (shortcut: Shortcuts[string]): void {
const shortcutValue = panelCommon.getShortcutValue(shortcut)
if (typeof shortcutValue !== 'number') return
panelCommon.doUpdateValue(shortcutValue, false)
panelCommon.clearPendingValue()
handleConfirmClick()
}
return { return {
dateArray: dateArrayRef, dateArray: dateArrayRef,
monthArray: monthArrayRef, monthArray: monthArrayRef,
@ -294,6 +311,8 @@ function useCalendar (
prevMonth, prevMonth,
handleNowClick, handleNowClick,
handleConfirmClick, handleConfirmClick,
handleSingleShortcutMouseenter,
handleSingleShortcutClick,
...validation, ...validation,
...panelCommon, ...panelCommon,
// datetime only // datetime only

View File

@ -14,7 +14,7 @@ import {
} from 'date-fns' } from 'date-fns'
import { dateArray, DateItem, strictParse } from '../utils' import { dateArray, DateItem, strictParse } from '../utils'
import { usePanelCommon } from './use-panel-common' import { usePanelCommon } from './use-panel-common'
import { datePickerInjectionKey } from '../interface' import { datePickerInjectionKey, Shortcuts } from '../interface'
const useDualCalendarProps = { const useDualCalendarProps = {
...usePanelCommon.props, ...usePanelCommon.props,
@ -529,6 +529,19 @@ function useDualCalendar (
function handleEndTimePickerChange (value: number): void { function handleEndTimePickerChange (value: number): void {
changeEndDateTime(value) changeEndDateTime(value)
} }
function handleRangeShortcutMouseenter (shortcut: Shortcuts[string]): void {
panelCommon.cachePendingValue()
const shortcutValue = panelCommon.getShortcutValue(shortcut)
if (!Array.isArray(shortcutValue)) return
changeStartEndTime(...shortcutValue)
}
function handleRangeShortcutClick (shortcut: Shortcuts[string]): void {
const shortcutValue = panelCommon.getShortcutValue(shortcut)
if (!Array.isArray(shortcutValue)) return
changeStartEndTime(...shortcutValue)
panelCommon.clearPendingValue()
handleConfirmClick()
}
return { return {
startDatesElRef, startDatesElRef,
endDatesElRef, endDatesElRef,
@ -554,6 +567,8 @@ function useDualCalendar (
weekdays: weekdaysRef, weekdays: weekdaysRef,
startDateArray: startDateArrayRef, startDateArray: startDateArrayRef,
endDateArray: endDateArrayRef, endDateArray: endDateArrayRef,
handleRangeShortcutMouseenter,
handleRangeShortcutClick,
...panelCommon, ...panelCommon,
...validation, ...validation,
// datetimerangeonly // datetimerangeonly

View File

@ -132,6 +132,15 @@ function usePanelCommon (props: UsePanelCommonProps) {
cached = false cached = false
} }
} }
function getShortcutValue (
shortcut: Shortcuts[string]
): number | [number, number] {
if (typeof shortcut === 'function') {
return shortcut()
}
return shortcut
}
return { return {
mergedTheme: mergedThemeRef, mergedTheme: mergedThemeRef,
mergedClsPrefix: mergedClsPrefixRef, mergedClsPrefix: mergedClsPrefixRef,
@ -150,7 +159,9 @@ function usePanelCommon (props: UsePanelCommonProps) {
handlePanelFocus, handlePanelFocus,
cachePendingValue, cachePendingValue,
clearPendingValue, clearPendingValue,
restorePendingValue restorePendingValue,
getShortcutValue,
handleShortcutMouseleave: restorePendingValue
} }
} }

View File

@ -76,6 +76,72 @@ describe('n-date-picker', () => {
wrapper.unmount() wrapper.unmount()
}) })
it('date type should work with shortcuts prop with function value', async () => {
const test = ref<Value>(0)
const wrapper = mount(NDatePicker, {
props: {
value: test.value,
type: 'date',
onUpdateValue: (value: Value) => {
test.value = value
},
shortcuts: {
'Honey birthday': () => 1631203200000
}
}
})
await wrapper.find('.n-input').trigger('click')
const button: HTMLElement = document
.querySelector('.n-date-panel-actions')
?.querySelector('.n-button') as HTMLElement
button.click()
expect(test.value).toEqual(1631203200000)
test.value = 0
wrapper.setProps({
type: 'datetime'
})
await wrapper.find('.n-input').trigger('click')
const timeButton: HTMLElement = document
.querySelector('.n-date-panel-actions')
?.querySelector('.n-button') as HTMLElement
timeButton.click()
expect(test.value).toEqual(1631203200000)
wrapper.unmount()
})
it('range type should work with shortcuts prop with function value', async () => {
const test = ref<Value>(0)
const wrapper = mount(NDatePicker, {
props: {
value: test.value,
type: 'daterange',
onUpdateValue: (value: Value) => {
test.value = value
},
shortcuts: {
'Honey birthday': () => [1629216000000, 1631203200000]
}
}
})
await wrapper.find('.n-input').trigger('click')
const button: HTMLElement = document
.querySelector('.n-date-panel-actions')
?.querySelector('.n-button') as HTMLElement
button.click()
expect(test.value).toEqual([1629216000000, 1631203200000])
test.value = 0
wrapper.setProps({
type: 'datetimerange'
})
await wrapper.find('.n-input').trigger('click')
const rangeButton: HTMLElement = document
.querySelector('.n-date-panel-actions')
?.querySelector('.n-button') as HTMLElement
rangeButton.click()
expect(test.value).toEqual([1629216000000, 1631203200000])
wrapper.unmount()
})
it('should work with `inputReadonly` prop', async () => { it('should work with `inputReadonly` prop', async () => {
const wrapper = mount(NDatePicker) const wrapper = mount(NDatePicker)
expect(wrapper.find('input').attributes('readonly')).not.toBe('') expect(wrapper.find('input').attributes('readonly')).not.toBe('')