refactor(date-picker): support vue3

This commit is contained in:
07akioni 2020-10-05 03:50:59 +08:00
parent f72f59567c
commit ab9ce35fc0
39 changed files with 1015 additions and 914 deletions

View File

@ -1,25 +1,25 @@
# Actions
```html
<n-date-picker
v-model="ts1"
v-model:value="ts1"
type="date"
:actions="['confirm']"
style="margin-bottom: 12px;"
/>
<n-date-picker
v-model="ts2"
v-model:value="ts2"
type="datetime"
style="margin-right: 12px; margin-bottom: 12px;"
:actions="['now']"
/>
<n-date-picker
v-model="range1"
v-model:value="range1"
type="daterange"
:actions="null"
style="margin-bottom: 12px;"
/>
<n-date-picker
v-model="range2"
v-model:value="range2"
type="datetimerange"
style="margin-bottom: 12px;"
:actions="['clear']"

View File

@ -1,7 +1,7 @@
# Date
```html
<n-date-picker
v-model="timestamp"
v-model:value="timestamp"
type="date"
clearable
/>

View File

@ -1,7 +1,7 @@
# Date Range
```html
<n-date-picker
v-model="range"
v-model:value="range"
type="daterange"
clearable
/>

View File

@ -1,7 +1,7 @@
# Datetime
```html
<n-date-picker
v-model="timestamp"
v-model:value="timestamp"
type="datetime"
clearable
/>

View File

@ -1,7 +1,7 @@
# Datetime Range
```html
<n-date-picker
v-model="range"
v-model:value="range"
type="datetimerange"
clearable
/>

View File

@ -1,23 +1,23 @@
# Disabled Specific Time
```html
<n-date-picker
v-model="timestamp1"
v-model:value="timestamp1"
type="date"
:is-date-disabled = "dateDisabled"
/>
<n-date-picker
v-model="timestamp2"
v-model:value="timestamp2"
type="datetime"
:is-date-disabled = "dateDisabled"
:is-time-disabled= "timeDisabled"
/>
<n-date-picker
v-model="timestamp3"
v-model:value="timestamp3"
type="daterange"
:is-date-disabled = "isRangeDateDisabled"
/>
<n-date-picker
v-model="timestamp4"
v-model:value="timestamp4"
type="datetimerange"
:is-date-disabled = "isRangeDateDisabled"
:is-time-disabled= "isRangeTimeDisabled"

View File

@ -1,26 +1,26 @@
# Disabled
```html
<n-date-picker
v-model="date"
v-model:value="date"
type="date"
:disabled="disabled"
/>
<n-date-picker
v-model="datetime"
v-model:value="datetime"
type="datetime"
:disabled="disabled"
/>
<n-date-picker
v-model="daterange"
v-model:value="daterange"
:disabled="disabled"
type="daterange"
/>
<n-date-picker
v-model="datetimerange"
v-model:value="datetimerange"
:disabled="disabled"
type="datetimerange"
/>
<n-switch v-model="disabled" />
<n-switch v-model:value="disabled" />
```
```js
export default {

View File

@ -1,34 +1,34 @@
# Events
```html
<n-date-picker
v-model="datetime"
v-model:value="datetime"
type="datetime"
:disabled="disabled"
@blur="onBlur1"
@change="onChange1"
@update:value="onChange1"
/>
<n-date-picker
v-model="date"
v-model:value="date"
type="date"
:disabled="disabled"
@blur="onBlur2"
@change="onChange2"
@update:value="onChange2"
/>
<n-date-picker
v-model="datetimerange"
v-model:value="datetimerange"
:disabled="disabled"
type="datetimerange"
@blur="onBlur3"
@change="onChange3"
@update:value="onChange3"
/>
<n-date-picker
v-model="daterange"
v-model:value="daterange"
:disabled="disabled"
type="daterange"
@blur="onBlur4"
@change="onChange4"
@update:value="onChange4"
/>
<n-switch v-model="disabled" />
<n-switch v-model:value="disabled" />
```
```js
export default {

View File

@ -1,12 +1,12 @@
# Format
```html
<n-date-picker
v-model="timestamp"
v-model:value="timestamp"
type="datetime"
clearable
:format="format"
/>
<n-date-picker v-model="timestamp2" type="datetime" :format="format" clearable />
<n-date-picker v-model:value="timestamp2" type="datetime" :format="format" clearable />
```
```js
export default {

View File

@ -2,17 +2,17 @@
Date Picker can be `small`, `medium` or `large` sized.
```html
<n-date-picker
v-model="timestamp"
v-model:value="timestamp"
size="small"
type="date"
/>
<n-date-picker
v-model="timestamp"
v-model:value="timestamp"
size="medium"
type="date"
/>
<n-date-picker
v-model="timestamp"
v-model:value="timestamp"
size="large"
type="date"
/>

View File

@ -1,25 +1,25 @@
# 操作
```html
<n-date-picker
v-model="ts1"
v-model:value="ts1"
type="date"
:actions="['confirm']"
style="margin-bottom: 12px;"
/>
<n-date-picker
v-model="ts2"
v-model:value="ts2"
type="datetime"
style="margin-right: 12px; margin-bottom: 12px;"
:actions="['now']"
/>
<n-date-picker
v-model="range1"
v-model:value="range1"
type="daterange"
:actions="null"
style="margin-bottom: 12px;"
/>
<n-date-picker
v-model="range2"
v-model:value="range2"
type="datetimerange"
style="margin-bottom: 12px;"
:actions="['clear']"

View File

@ -1,7 +1,7 @@
# 日期
```html
<n-date-picker
v-model="timestamp"
v-model:value="timestamp"
type="date"
clearable
/>

View File

@ -1,7 +1,7 @@
# 日期范围
```html
<n-date-picker
v-model="range"
v-model:value="range"
type="daterange"
clearable
/>

View File

@ -1,7 +1,7 @@
# 日期时间
```html
<n-date-picker
v-model="timestamp"
v-model:value="timestamp"
type="datetime"
clearable
/>

View File

@ -1,7 +1,7 @@
# 日期时间范围
```html
<n-date-picker
v-model="range"
v-model:value="range"
type="datetimerange"
clearable
/>

View File

@ -1,23 +1,23 @@
# 禁用特定时间
```html
<n-date-picker
v-model="timestamp1"
v-model:value="timestamp1"
type="date"
:is-date-disabled = "dateDisabled"
/>
<n-date-picker
v-model="timestamp2"
v-model:value="timestamp2"
type="datetime"
:is-date-disabled = "dateDisabled"
:is-time-disabled= "timeDisabled"
/>
<n-date-picker
v-model="timestamp3"
v-model:value="timestamp3"
type="daterange"
:is-date-disabled = "isRangeDateDisabled"
/>
<n-date-picker
v-model="timestamp4"
v-model:value="timestamp4"
type="datetimerange"
:is-date-disabled = "isRangeDateDisabled"
:is-time-disabled= "isRangeTimeDisabled"

View File

@ -1,26 +1,26 @@
# 禁用
```html
<n-date-picker
v-model="date"
v-model:value="date"
type="date"
:disabled="disabled"
/>
<n-date-picker
v-model="datetime"
v-model:value="datetime"
type="datetime"
:disabled="disabled"
/>
<n-date-picker
v-model="daterange"
v-model:value="daterange"
:disabled="disabled"
type="daterange"
/>
<n-date-picker
v-model="datetimerange"
v-model:value="datetimerange"
:disabled="disabled"
type="datetimerange"
/>
<n-switch v-model="disabled" />
<n-switch v-model:value="disabled" />
```
```js
export default {

View File

@ -1,34 +1,34 @@
# 事件
```html
<n-date-picker
v-model="datetime"
v-model:value="datetime"
type="datetime"
:disabled="disabled"
@blur="onBlur1"
@change="onChange1"
@update:value="onChange1"
/>
<n-date-picker
v-model="date"
v-model:value="date"
type="date"
:disabled="disabled"
@blur="onBlur2"
@change="onChange2"
@update:value="onChange2"
/>
<n-date-picker
v-model="datetimerange"
v-model:value="datetimerange"
:disabled="disabled"
type="datetimerange"
@blur="onBlur3"
@change="onChange3"
@update:value="onChange3"
/>
<n-date-picker
v-model="daterange"
v-model:value="daterange"
:disabled="disabled"
type="daterange"
@blur="onBlur4"
@change="onChange4"
@update:value="onChange4"
/>
<n-switch v-model="disabled" />
<n-switch v-model:value="disabled" />
```
```js
export default {

View File

@ -1,12 +1,12 @@
# 格式化
```html
<n-date-picker
v-model="timestamp"
v-model:value="timestamp"
type="datetime"
clearable
:format="format"
/>
<n-date-picker v-model="timestamp2" type="datetime" :format="format" clearable />
<n-date-picker v-model:value="timestamp2" type="datetime" :format="format" clearable />
```
```js
export default {

View File

@ -14,10 +14,6 @@ actions
events
format
```
## V-model
|Prop|Event|
|-|-|
|value|change|
## Props
### 通用的 Props
@ -29,6 +25,8 @@ format
|theme|`'light' \| 'dark' \| null \| string`|`null`||
|type|`'date' \| 'datetime' \| 'daterange' \|'datetimerange'`|`'date`||
|value|`number`|`null`||
|on-blur|`() => any`|`undefined`||
|on-focus|`() => any`|`undefined`||
### Date 类型的 Props
|名称|类型|默认值|说明|
@ -37,6 +35,7 @@ format
|format|`string`|`'yyyy-MM-dd'`||
|is-date-disabled|`(current: number) => boolean`|`() => false`||
|placeholder|`string`|`'Select Date'`||
|on-change|`(value: number \| null) => any`|`undefined`||
### DateTime 类型的 Props
|名称|类型|默认值|说明|
@ -46,6 +45,7 @@ format
|is-date-disabled|`(current: number) => boolean`|`() => false`||
|is-time-disabled|`(current: number) => { isHourDisabled: boolean, isMinuteDisabled: boolean, isSecondDisabled: boolean }`|`() => ({ isHourDisabled: () => false, isMinuteDisabled: () => false, isSecondDisabled: () => false }})`||
|placeholder|`string`|`'Select Date and Time'`||
|on-change|`(value: number \| null) => any`|`undefined`||
### DateRange 类型的 Props
|名称|类型|默认值|说明|
@ -57,7 +57,7 @@ format
|is-time-disabled|`(current: number) => { isHourDisabled: boolean, isMinuteDisabled: boolean, isSecondDisabled: boolean }`|`() => ({ isHourDisabled: () => false, isMinuteDisabled: () => false, isSecondDisabled: () => false }})`||
|separator|`string`|`'至'`||
|start-placeholder|`string`|`'Start Date'`||
|on-change|`(value: [number, number] \| null) => any`|`undefined`||
### DateTimeRange 类型的 Props
|名称|类型|默认值|说明|
@ -69,17 +69,4 @@ format
|is-time-disabled|`(current: number) => { isHourDisabled: boolean, isMinuteDisabled: boolean, isSecondDisabled: boolean }`|`() => ({ isHourDisabled: () => false, isMinuteDisabled: () => false, isSecondDisabled: () => false }})`||
|separator|`string`|`'to'`||
|start-placeholder|`string`|`'Start Date and Time'`||
## Events
### Date, DateTime 类型的 Events
|名称|参数|说明|
|-|-|-|
|blur|`()`||
|change|`(value: number \| null)`||
### DateRange, DateTimeRange 类型的 Events
|名称|参数|说明|
|-|-|-|
|blur|`()`||
|change|`(value: [number, number] \| null)`||
|on-change|`(value: [number, number] \| null) => any`|`undefined`||

View File

@ -2,17 +2,17 @@
`small`、`medium` 和 `large` 尺寸。
```html
<n-date-picker
v-model="timestamp"
v-model:value="timestamp"
size="small"
type="date"
/>
<n-date-picker
v-model="timestamp"
v-model:value="timestamp"
size="medium"
type="date"
/>
<n-date-picker
v-model="timestamp"
v-model:value="timestamp"
size="large"
type="date"
/>

View File

@ -1,2 +1,3 @@
export { default as ChevronRightIcon } from './ChevronRight.vue'
export { default as CheckmarkIcon } from './Checkmark.vue'
export { default as CalendarIcon } from './CalendarIcon.vue'

View File

@ -19,10 +19,6 @@ export default {
}
},
mixins: [withapp, themeable],
model: {
prop: 'value',
event: 'input'
},
emits: ['input'],
props: {
name: {

View File

@ -462,7 +462,7 @@ export default {
},
handleActivatorBlur () {
this.doBlur()
// this.closeMenu()
this.closeMenu()
},
handleActivatorClick () {
if (this.filterable) {

View File

@ -60,10 +60,6 @@ export default {
mixins: [
usecssr(styles)
],
model: {
prop: 'checked',
event: 'chanage'
},
props: {
value: {
validator: createValidator(['number', 'boolean', 'string']),

View File

@ -1,6 +1,6 @@
<template>
<div
ref="activator"
ref="triggerRef"
class="n-date-picker"
:class="{
[`n-${syntheticTheme}-theme`]: syntheticTheme,
@ -14,7 +14,7 @@
>
<n-input
v-if="isRange"
ref="input"
ref="inputRef"
:size="syntheticSize"
:theme="syntheticTheme"
passively-activated
@ -41,8 +41,8 @@
</n-input>
<n-input
v-else
ref="input"
v-model="displayTime"
ref="inputRef"
v-model:value="displayTime"
:theme="syntheticTheme"
passively-activated
:size="syntheticSize"
@ -64,92 +64,110 @@
<n-icon><calendar-icon /></n-icon>
</template>
</n-input>
<div
ref="contentContainer"
class="n-positioning-container"
:class="{
[namespace]: namespace
}"
<n-base-lazy-teleport
:show="active"
>
<div
ref="content"
class="n-positioning-content"
@keydown.esc.enter="handlePanelKeyDownEsc"
@keydown="handleKeyDown"
ref="offsetContainerRef"
v-zindexable="{ enabled: active }"
class="n-positioning-container"
:class="{
[namespace]: namespace
}"
>
<datetime-panel
v-if="type === 'datetime'"
ref="panel"
v-clickoutside="handleClickOutside"
:value="value"
:active="active"
:actions="actions"
:theme="syntheticTheme"
:is-date-disabled="isDateDisabled"
:is-time-disabled="isTimeDisabled"
:format="computedFormat"
@tab-out="handlePanelTabOut"
@input="handlePanelInput"
@close="handlePanelClose"
/>
<date-panel
v-else-if="type === 'date'"
ref="panel"
v-clickoutside="handleClickOutside"
:value="value"
:active="active"
:actions="actions"
:theme="syntheticTheme"
:is-date-disabled="isDateDisabled"
@input="handlePanelInput"
@tab-out="handlePanelTabOut"
@close="handlePanelClose"
/>
<daterange-panel
v-else-if="type === 'daterange'"
ref="panel"
v-clickoutside="handleClickOutside"
:value="value"
:active="active"
:actions="actions"
:theme="syntheticTheme"
:is-date-disabled="isDateDisabled"
@input="handleRangePanelInput"
@tab-out="handlePanelTabOut"
@close="handlePanelClose"
@check-value="setInvalidStatus"
/>
<datetimerange-panel
v-else-if="type === 'datetimerange'"
ref="panel"
v-clickoutside="handleClickOutside"
:format="computedFormat"
:value="value"
:active="active"
:actions="actions"
:theme="syntheticTheme"
:is-date-disabled="isDateDisabled"
:is-time-disabled="isTimeDisabled"
@input="handleRangePanelInput"
@close="handlePanelClose"
@tab-out="handlePanelTabOut"
@check-value="setInvalidStatus"
/>
<div
ref="trackingRef"
class="n-positioning-content"
@keydown.esc="handlePanelKeyDownEsc"
@keydown="handleKeyDown"
>
<transition
name="n-fade-in-scale-up-transition"
:appear="isMounted"
>
<datetime-panel
v-if="type === 'datetime' && active"
ref="panelRef"
v-clickoutside="handleClickOutside"
:value="value"
:active="active"
:actions="actions"
:theme="syntheticTheme"
:is-date-disabled="isDateDisabled"
:is-time-disabled="isTimeDisabled"
:format="computedFormat"
@update:value="handlePanelInput"
@tab-out="handlePanelTabOut"
@close="handlePanelClose"
/>
<date-panel
v-else-if="type === 'date' && active"
ref="panelRef"
v-clickoutside="handleClickOutside"
:value="value"
:active="active"
:actions="actions"
:theme="syntheticTheme"
:is-date-disabled="isDateDisabled"
@update:value="handlePanelInput"
@tab-out="handlePanelTabOut"
@close="handlePanelClose"
/>
<daterange-panel
v-else-if="type === 'daterange' && active"
ref="panelRef"
v-clickoutside="handleClickOutside"
:value="value"
:active="active"
:actions="actions"
:theme="syntheticTheme"
:is-date-disabled="isDateDisabled"
@update:value="handleRangePanelInput"
@tab-out="handlePanelTabOut"
@close="handlePanelClose"
/>
<datetimerange-panel
v-else-if="type === 'datetimerange' && active"
ref="panelRef"
v-clickoutside="handleClickOutside"
:format="computedFormat"
:value="value"
:active="active"
:actions="actions"
:theme="syntheticTheme"
:is-date-disabled="isDateDisabled"
:is-time-disabled="isTimeDisabled"
@update:value="handleRangePanelInput"
@close="handlePanelClose"
@tab-out="handlePanelTabOut"
/>
</transition>
</div>
</div>
</div>
</n-base-lazy-teleport>
</div>
</template>
<script>
import detachable from '../../_mixins/detachable'
import placeable from '../../_mixins/placeable'
import zindexable from '../../_mixins/zindexable'
import withapp from '../../_mixins/withapp'
import themeable from '../../_mixins/themeable'
import asformitem from '../../_mixins/asformitem'
import usecssr from '../../_mixins/usecssr'
import clickoutside from '../../_directives/clickoutside'
import locale from '../../_mixins/locale'
import {
ref
} from 'vue'
import {
configurable,
themeable,
placeable,
asformitem,
usecssr,
locale
} from '../../_mixins'
import {
clickoutside,
zindexable
} from '../../_directives'
import { warn } from '../../_utils/naive'
import { call } from '../../_utils/vue'
import { useIsMounted } from '../../_utils/composition'
import DatetimePanel from './panel/datetime.vue'
import DatetimerangePanel from './panel/datetimerange.vue'
import DatePanel from './panel/date.vue'
@ -157,7 +175,8 @@ import DaterangePanel from './panel/daterange.vue'
import NInput from '../../input'
import NIcon from '../../icon'
import CalendarIcon from './CalendarIcon.vue'
import { CalendarIcon } from '../../_base/icons'
import NBaseLazyTeleport from '../../_base/lazy-teleport'
import format from 'date-fns/format'
import getTime from 'date-fns/getTime'
@ -177,9 +196,11 @@ const DATE_FORMAT = {
export default {
name: 'DatePicker',
directives: {
clickoutside
clickoutside,
zindexable
},
components: {
NBaseLazyTeleport,
NInput,
NIcon,
DatetimePanel,
@ -189,19 +210,13 @@ export default {
CalendarIcon
},
mixins: [
withapp,
configurable,
themeable,
detachable,
placeable,
zindexable,
locale('DatePicker'),
asformitem(),
usecssr(styles)
],
model: {
prop: 'value',
event: 'change'
},
provide () {
return {
NDatePicker: this
@ -267,6 +282,32 @@ export default {
isTimeDisabled: {
type: Function,
default: undefined
},
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:value': {
type: [Function, Array],
default: undefined
},
onFocus: {
type: [Function, Array],
default: undefined
},
onBlur: {
type: [Function, Array],
default: undefined
},
// deprecated
onChange: {
validator () {
if (__DEV__) {
warn(
'data-picker',
'`on-change` is deprecated, please use `on-update:value` instead.'
)
}
return true
},
default: undefined
}
},
data () {
@ -280,12 +321,25 @@ export default {
isStartValueInvalid: false
}
},
setup () {
return {
panelRef: ref(null),
offsetContainerRef: ref(null),
trackingRef: ref(null),
triggerRef: ref(null),
inputRef: ref(null),
isMounted: useIsMounted()
}
},
computed: {
__placeableEnabled () {
return this.active
},
isRange () {
return ['daterange', 'datetimerange'].includes(this.type)
},
localizedSeperator () {
if (this.sepearator !== undefined) return this.separator
if (this.separator !== undefined) return this.separator
return this.localeNamespace.separator
},
localizedPlacehoder () {
@ -341,18 +395,58 @@ export default {
this.refresh(this.value)
},
methods: {
__placeableOffsetContainer () {
return this.offsetContainerRef
},
__placeableTracking () {
return this.trackingRef
},
__placeableTracked () {
return this.triggerRef
},
__placeableBody () {
return this.panelRef
},
doUpdateValue (...args) {
const {
'onUpdate:value': onUpdateValue,
onChange,
__triggerFormChange,
__triggerFormInput
} = this
if (onUpdateValue) call(onUpdateValue, ...args)
if (onChange) call(onChange, ...args)
__triggerFormChange()
__triggerFormInput()
},
doFocus (...args) {
const {
onFocus,
__triggerFormFocus
} = this
if (onFocus) call(onFocus, ...args)
__triggerFormFocus()
},
doBlur (...args) {
const {
onBlur,
__triggerFormBlur
} = this
if (onBlur) call(onBlur, ...args)
__triggerFormBlur()
},
handleKeyDown (e) {
const value = this.value
if (this.type === 'date') {
const nextValue = getDerivedTimeFromKeyboardEvent(value, e)
if (value !== nextValue) {
this.$emit('change', nextValue)
this.doUpdateValue(nextValue)
}
}
},
handleClear (e) {
e.stopPropagation()
this.$emit('change', null)
this.doUpdateValue(null)
this.refresh(null)
},
handlePanelTabOut () {
@ -362,7 +456,7 @@ export default {
})
},
handleClickOutside (e) {
if (this.active && !this.$refs.activator.contains(e.target)) {
if (this.active && !this.triggerRef.contains(e.target)) {
this.closeCalendar({
returnFocus: false,
emitBlur: true
@ -385,11 +479,11 @@ export default {
* Panel Input
*/
handlePanelInput (value, valueString) {
this.$emit('change', value)
this.doUpdateValue(value)
this.refresh(value)
},
handleRangePanelInput (value, valueString) {
this.$emit('change', value)
this.doUpdateValue(value)
this.refresh(value)
},
/**
@ -428,12 +522,15 @@ export default {
* input deactivate & blur
*/
handleInputBlur (e) {
const {
panelRef
} = this
if (!(
this.$refs.panel &&
this.$refs.panel.$el.contains(e.relatedTarget)
panelRef &&
panelRef.$el.contains(e.relatedTarget)
)) {
this.cleanValue()
this.$emit('blur')
this.doBlur()
this.closeCalendar({
returnFocus: false,
emitBlur: false
@ -468,7 +565,7 @@ export default {
} else {
const newSelectedDateTime = strictParse(this.displayTime, this.computedFormat, new Date())
if (isValid(newSelectedDateTime)) {
this.$emit('change', getTime(newSelectedDateTime))
this.doUpdateValue(getTime(newSelectedDateTime))
} else {
this.refreshDisplayTime(this.value)
}
@ -480,7 +577,7 @@ export default {
handleTimeInput (v) {
const newSelectedDateTime = strictParse(this.displayTime, this.computedFormat, new Date())
if (isValid(newSelectedDateTime)) {
this.$emit('change', getTime(newSelectedDateTime))
this.doUpdateValue(getTime(newSelectedDateTime))
}
},
handleRangeInput (v, isValueInvalid) {
@ -511,7 +608,7 @@ export default {
*/
handleInputFocus () {
if (this.disabled) return
this.$emit('focus')
this.doFocus()
},
/**
* Calendar
@ -519,7 +616,7 @@ export default {
openCalendar (e) {
if (this.disabled || this.active) return
this.active = true
this.$nextTick().then(this.updatePosition)
this.$nextTick(this.__placeableSyncPosition)
},
closeCalendar ({
returnFocus,
@ -528,12 +625,15 @@ export default {
if (this.active) {
this.active = false
if (returnFocus) {
if (this.$refs.input) {
this.$refs.input.focus()
const {
inputRef
} = this
if (inputRef) {
inputRef.focus()
}
}
if (emitBlur) {
this.$emit('blur')
this.doBlur()
}
}
},
@ -545,12 +645,12 @@ export default {
time = getTime(time)
}
if (this.value === null) {
this.$emit('change', [time, time])
this.doUpdateValue([time, time])
this.refresh([time, time])
} else {
const newValue = [time, Math.max(this.value[1], time)]
if (!isEqual(newValue, this.value)) {
this.$emit('change', newValue)
this.doUpdateValue(newValue)
this.refresh(newValue)
}
}
@ -560,12 +660,12 @@ export default {
time = getTime(time)
}
if (this.value === null) {
this.$emit('change', [time, time])
this.doUpdateValue([time, time])
this.refresh([time, time])
} else {
const newValue = [Math.min(this.value[0], time), time]
if (!isEqual(newValue, this.value)) {
this.$emit('change', newValue)
this.doUpdateValue(newValue)
this.refresh(newValue)
}
}
@ -578,7 +678,7 @@ export default {
if (typeof endTime !== 'number') {
endTime = getTime(endTime)
}
this.$emit('change', [startTime, endTime])
this.doUpdateValue([startTime, endTime])
this.refresh([startTime, endTime])
},
setInvalidStatus (isValueInvalid) {

View File

@ -1,6 +1,7 @@
import clickoutside from '../../../_directives/clickoutside'
import focusDetector from '../../../_base/focus-detector'
import locale from '../../../_mixins/locale'
import FocusDetector from '../../../_base/focus-detector'
import {
locale
} from '../../../_mixins'
import keyboardDelegate from '../../../_utils/delegate/keyboardDelegate'
import { KEY_CODE } from '../../../_utils/event/keyCode'
@ -19,40 +20,85 @@ export default {
default: null
}
},
directives: {
clickoutside
},
components: {
focusDetector
FocusDetector
},
props: {
theme: {
type: String,
default: null
},
active: {
type: Boolean,
default: undefined
},
onConfirm: {
type: Function,
default: undefined
},
'onUpdate:value': {
type: Function,
default: undefined
},
onClose: {
type: Function,
default: undefined
},
onTabOut: {
type: Function,
default: undefined
}
},
data () {
return {
noTransition: false,
memorizedValue: null,
memorizedValue: this.value,
...TIME_CONST
}
},
created () {
this.memorizedValue = this.value
},
computed: {
weekdays () {
return weekdays.map(weekday => this.localeNamespace[weekday])
}
},
methods: {
doConfirm (...args) {
const {
onConfirm
} = this
if (onConfirm) onConfirm(...args)
},
doUpdateValue (...args) {
const {
'onUpdate:value': onUpdateValue
} = this
if (onUpdateValue) onUpdateValue(...args)
},
doClose (...args) {
const {
onClose
} = this
if (onClose) onClose(...args)
},
doTabOut (...args) {
const {
onTabOut
} = this
if (onTabOut) onTabOut(...args)
},
clearValue () {
this.$emit('input', null)
this.doUpdateValue(null)
},
handleFocusDetectorFocus (e) {
this.$emit('tab-out', e)
this.doTabOut(e)
},
disableTransitionOneTick () {
if (this.active) {
this.noTransition = true
this.$nextTick().then(() => {
if (this.$el) {
this.$el.getBoundingClientRect()
this.$nextTick(() => {
const el = this.$el
if (el) {
void el.offsetWidth
}
this.noTransition = false
})
@ -67,7 +113,7 @@ export default {
keyboardDelegate.shiftPressed
) {
e.preventDefault()
this.$emit('tab-out')
this.doTabOut()
}
},
handlePanelFocus (e) {
@ -76,7 +122,7 @@ export default {
e.target === this.$el &&
this.$el.contains(e.relatedTarget)
) {
this.$emit('tab-out')
this.doTabOut()
}
}
}

View File

@ -1,110 +1,107 @@
<template>
<transition name="n-fade-in-scale-up-transition">
<div
v-if="active"
tabindex="0"
class="n-date-panel n-date-panel--date"
:class="{
[`n-${theme}-theme`]: theme
}"
@focus="handlePanelFocus"
@keydown="handlePanelKeyDown"
>
<div class="n-date-panel-calendar">
<div class="n-date-panel-month">
<div
class="n-date-panel-month__fast-prev"
@click="prevYear"
>
<n-base-icon type="fast-backward" />
</div>
<div
class="n-date-panel-month__prev"
@click="prevMonth"
>
<n-base-icon type="backward" />
</div>
<div class="n-date-panel-month__month-year">
{{ calendarMonth }} {{ calendarYear }}
</div>
<div
class="n-date-panel-month__next"
@click="nextMonth"
>
<n-base-icon type="forward" />
</div>
<div
class="n-date-panel-month__fast-next"
@click="nextYear"
>
<n-base-icon type="fast-forward" />
</div>
<div
tabindex="0"
class="n-date-panel n-date-panel--date"
:class="{
[`n-${theme}-theme`]: theme
}"
@focus="handlePanelFocus"
@keydown="handlePanelKeyDown"
>
<div class="n-date-panel-calendar">
<div class="n-date-panel-month">
<div
class="n-date-panel-month__fast-prev"
@click="prevYear"
>
<n-base-icon type="fast-backward" />
</div>
<div class="n-date-panel-weekdays">
<div
v-for="weekday in weekdays"
:key="weekday"
class="n-date-panel-weekdays__day"
>
{{ weekday }}
</div>
<div
class="n-date-panel-month__prev"
@click="prevMonth"
>
<n-base-icon type="backward" />
</div>
<div class="n-date-panel-dates">
<div
v-for="(dateItem, i) in dateArray"
:key="i"
class="n-date-panel-date"
:class="{
'n-date-panel-date--current': dateItem.isCurrentDate,
'n-date-panel-date--selected': dateItem.isSelectedDate,
'n-date-panel-date--excluded': !dateItem.isDateOfDisplayMonth,
'n-date-panel-date--transition-disabled': noTransition,
'n-date-panel-date--disabled': isDateDisabled(dateItem.timestamp)
}"
@click="handleDateClick(dateItem)"
>
{{ dateItem.dateObject.date }}
</div>
<div
v-if="!(actions && actions.length)"
style="height: 8px; width: 100%;"
/>
<div class="n-date-panel-month__month-year">
{{ calendarMonth }} {{ calendarYear }}
</div>
<div
class="n-date-panel-month__next"
@click="nextMonth"
>
<n-base-icon type="forward" />
</div>
<div
class="n-date-panel-month__fast-next"
@click="nextYear"
>
<n-base-icon type="fast-forward" />
</div>
</div>
<div
v-if="actions && actions.length"
class="n-date-panel-actions"
>
<n-button
v-if="actions.includes('clear')"
:theme="theme"
size="tiny"
@click="clearValue"
<div class="n-date-panel-weekdays">
<div
v-for="weekday in weekdays"
:key="weekday"
class="n-date-panel-weekdays__day"
>
{{ localeNamespace.clear }}
</n-button>
<n-button
v-if="actions.includes('now')"
:theme="theme"
size="tiny"
@click="setSelectedDateTimeToNow"
>
{{ localeNamespace.now }}
</n-button>
<n-button
v-if="actions.includes('confirm')"
:theme="theme"
size="tiny"
type="primary"
:disabled="isDateInvalid"
@click="handleConfirmClick"
>
{{ localeNamespace.confirm }}
</n-button>
{{ weekday }}
</div>
</div>
<div class="n-date-panel-dates">
<div
v-for="(dateItem, i) in dateArray"
:key="i"
class="n-date-panel-date"
:class="{
'n-date-panel-date--current': dateItem.isCurrentDate,
'n-date-panel-date--selected': dateItem.isSelectedDate,
'n-date-panel-date--excluded': !dateItem.isDateOfDisplayMonth,
'n-date-panel-date--transition-disabled': noTransition,
'n-date-panel-date--disabled': isDateDisabled(dateItem.timestamp)
}"
@click="handleDateClick(dateItem)"
>
{{ dateItem.dateObject.date }}
</div>
<div
v-if="!(actions && actions.length)"
style="height: 8px; width: 100%;"
/>
</div>
<focus-detector @focus="handleFocusDetectorFocus" />
</div>
</transition>
<div
v-if="actions && actions.length"
class="n-date-panel-actions"
>
<n-button
v-if="actions.includes('clear')"
:theme="theme"
size="tiny"
@click="clearValue"
>
{{ localeNamespace.clear }}
</n-button>
<n-button
v-if="actions.includes('now')"
:theme="theme"
size="tiny"
@click="setSelectedDateTimeToNow"
>
{{ localeNamespace.now }}
</n-button>
<n-button
v-if="actions.includes('confirm')"
:theme="theme"
size="tiny"
type="primary"
:disabled="isDateInvalid"
@click="handleConfirmClick"
>
{{ localeNamespace.confirm }}
</n-button>
</div>
<focus-detector @focus="handleFocusDetectorFocus" />
</div>
</template>
<script>

View File

@ -1,177 +1,174 @@
<template>
<transition name="n-fade-in-scale-up-transition">
<div
tabindex="0"
class="n-date-panel n-date-panel--daterange"
:class="{
[`n-${theme}-theme`]: theme
}"
@click.capture="resetSelectingStatus"
@keydown="handlePanelKeyDown"
@focus="handlePanelFocus"
>
<div
v-if="active"
tabindex="0"
class="n-date-panel n-date-panel--daterange"
:class="{
[`n-${theme}-theme`]: theme
}"
@click.capture="resetSelectingStatus"
@keydown="handlePanelKeyDown"
@focus="handlePanelFocus"
ref="startDates"
class="n-date-panel-calendar n-date-panel-calendar--start"
>
<div
ref="startDates"
class="n-date-panel-calendar n-date-panel-calendar--start"
>
<div class="n-date-panel-month">
<div
class="n-date-panel-month__fast-prev"
@click="startCalendarPrevYear"
>
<n-base-icon type="fast-backward" />
</div>
<div
class="n-date-panel-month__prev"
@click="startCalendarPrevMonth"
>
<n-base-icon type="backward" />
</div>
<div class="n-date-panel-month__month-year">
{{ startCalendarMonth }} {{ startCalendarYear }}
</div>
<div
class="n-date-panel-month__next"
@click="startCalendarNextMonth"
>
<n-base-icon type="forward" />
</div>
<div
class="n-date-panel-month__fast-next"
@click="startCalendarNextYear"
>
<n-base-icon type="fast-forward" />
</div>
</div>
<div class="n-date-panel-weekdays">
<div
v-for="weekday in weekdays"
:key="weekday"
class="n-date-panel-weekdays__day"
>
{{ weekday }}
</div>
</div>
<div class="n-date-panel__divider" />
<div class="n-date-panel-month">
<div
class="n-date-panel-dates"
class="n-date-panel-month__fast-prev"
@click="startCalendarPrevYear"
>
<div
v-for="(dateItem, i) in startDateArray"
:key="i"
class="n-date-panel-date"
:class="{
'n-date-panel-date--current': dateItem.isCurrentDate,
'n-date-panel-date--selected': dateItem.isSelectedDate,
'n-date-panel-date--excluded': !dateItem.isDateOfDisplayMonth,
'n-date-panel-date--covered': dateItem.isInSpan,
'n-date-panel-date--transition-disabled': noTransition,
'n-date-panel-date--disabled': isCalendarDateDisabled(dateItem.timestamp)
}"
@click="handleDateClick(dateItem)"
@mouseenter="handleDateMouseEnter(dateItem)"
>
{{ dateItem.dateObject.date }}
</div>
<n-base-icon type="fast-backward" />
</div>
</div>
<div class="n-date-panel__vertical-divider" />
<div
ref="endDates"
class="n-date-panel-calendar n-date-panel-calendar--end"
>
<div class="n-date-panel-month">
<div
class="n-date-panel-month__fast-prev"
@click="endCalendarPrevYear"
>
<n-base-icon type="fast-backward" />
</div>
<div
class="n-date-panel-month__prev"
@click="endCalendarPrevMonth"
>
<n-base-icon type="backward" />
</div>
<div class="n-date-panel-month__month-year">
{{ endCalendarMonth }} {{ endCalendarYear }}
</div>
<div
class="n-date-panel-month__next"
@click="endCalendarNextMonth"
>
<n-base-icon type="forward" />
</div>
<div
class="n-date-panel-month__fast-next"
@click="endCalendarNextYear"
>
<n-base-icon type="fast-forward" />
</div>
</div>
<div class="n-date-panel-weekdays">
<div
v-for="weekday in weekdays"
:key="weekday"
class="n-date-panel-weekdays__day"
>
{{ weekday }}
</div>
</div>
<div class="n-date-panel__divider" />
<div
class="n-date-panel-dates"
class="n-date-panel-month__prev"
@click="startCalendarPrevMonth"
>
<div
v-for="(dateItem, i) in endDateArray"
:key="i"
class="n-date-panel-date"
:class="{
'n-date-panel-date--current': dateItem.isCurrentDate,
'n-date-panel-date--selected': dateItem.isSelectedDate,
'n-date-panel-date--excluded': !dateItem.isDateOfDisplayMonth,
'n-date-panel-date--covered': dateItem.isInSpan,
'n-date-panel-date--transition-disabled': noTransition,
'n-date-panel-date--disabled': isCalendarDateDisabled(dateItem.timestamp)
}"
@click="handleDateClick(dateItem)"
@mouseenter="handleDateMouseEnter(dateItem)"
>
{{ dateItem.dateObject.date }}
</div>
<div
v-if="!(actions && actions.length)"
style="height: 8px; width: 100%;"
/>
<n-base-icon type="backward" />
</div>
<div class="n-date-panel-month__month-year">
{{ startCalendarMonth }} {{ startCalendarYear }}
</div>
<div
class="n-date-panel-month__next"
@click="startCalendarNextMonth"
>
<n-base-icon type="forward" />
</div>
<div
class="n-date-panel-month__fast-next"
@click="startCalendarNextYear"
>
<n-base-icon type="fast-forward" />
</div>
</div>
<div
v-if="actions && actions.length"
class="n-date-panel-actions"
>
<n-button
v-if="actions.includes('clear')"
:theme="theme"
size="tiny"
@click="clearValue"
<div class="n-date-panel-weekdays">
<div
v-for="weekday in weekdays"
:key="weekday"
class="n-date-panel-weekdays__day"
>
{{ localeNamespace.clear }}
</n-button>
<n-button
v-if="actions.includes('confirm')"
:theme="theme"
size="tiny"
type="primary"
:disabled="isRangeInvalid"
@click="handleConfirmClick"
>
{{ localeNamespace.confirm }}
</n-button>
{{ weekday }}
</div>
</div>
<div class="n-date-panel__divider" />
<div
class="n-date-panel-dates"
>
<div
v-for="(dateItem, i) in startDateArray"
:key="i"
class="n-date-panel-date"
:class="{
'n-date-panel-date--current': dateItem.isCurrentDate,
'n-date-panel-date--selected': dateItem.isSelectedDate,
'n-date-panel-date--excluded': !dateItem.isDateOfDisplayMonth,
'n-date-panel-date--covered': dateItem.isInSpan,
'n-date-panel-date--transition-disabled': noTransition,
'n-date-panel-date--disabled': isCalendarDateDisabled(dateItem.timestamp)
}"
@click="handleDateClick(dateItem)"
@mouseenter="handleDateMouseEnter(dateItem)"
>
{{ dateItem.dateObject.date }}
</div>
</div>
<focus-detector @focus="handleFocusDetectorFocus" />
</div>
</transition>
<div class="n-date-panel__vertical-divider" />
<div
ref="endDates"
class="n-date-panel-calendar n-date-panel-calendar--end"
>
<div class="n-date-panel-month">
<div
class="n-date-panel-month__fast-prev"
@click="endCalendarPrevYear"
>
<n-base-icon type="fast-backward" />
</div>
<div
class="n-date-panel-month__prev"
@click="endCalendarPrevMonth"
>
<n-base-icon type="backward" />
</div>
<div class="n-date-panel-month__month-year">
{{ endCalendarMonth }} {{ endCalendarYear }}
</div>
<div
class="n-date-panel-month__next"
@click="endCalendarNextMonth"
>
<n-base-icon type="forward" />
</div>
<div
class="n-date-panel-month__fast-next"
@click="endCalendarNextYear"
>
<n-base-icon type="fast-forward" />
</div>
</div>
<div class="n-date-panel-weekdays">
<div
v-for="weekday in weekdays"
:key="weekday"
class="n-date-panel-weekdays__day"
>
{{ weekday }}
</div>
</div>
<div class="n-date-panel__divider" />
<div
class="n-date-panel-dates"
>
<div
v-for="(dateItem, i) in endDateArray"
:key="i"
class="n-date-panel-date"
:class="{
'n-date-panel-date--current': dateItem.isCurrentDate,
'n-date-panel-date--selected': dateItem.isSelectedDate,
'n-date-panel-date--excluded': !dateItem.isDateOfDisplayMonth,
'n-date-panel-date--covered': dateItem.isInSpan,
'n-date-panel-date--transition-disabled': noTransition,
'n-date-panel-date--disabled': isCalendarDateDisabled(dateItem.timestamp)
}"
@click="handleDateClick(dateItem)"
@mouseenter="handleDateMouseEnter(dateItem)"
>
{{ dateItem.dateObject.date }}
</div>
<div
v-if="!(actions && actions.length)"
style="height: 8px; width: 100%;"
/>
</div>
</div>
<div
v-if="actions && actions.length"
class="n-date-panel-actions"
>
<n-button
v-if="actions.includes('clear')"
:theme="theme"
size="tiny"
@click="clearValue"
>
{{ localeNamespace.clear }}
</n-button>
<n-button
v-if="actions.includes('confirm')"
:theme="theme"
size="tiny"
type="primary"
:disabled="isRangeInvalid"
@click="handleConfirmClick"
>
{{ localeNamespace.confirm }}
</n-button>
</div>
<focus-detector @focus="handleFocusDetectorFocus" />
</div>
</template>
<script>

View File

@ -1,143 +1,139 @@
<template>
<transition name="n-fade-in-scale-up-transition">
<div
tabindex="0"
class="n-date-panel n-date-panel--datetime"
:class="{
[`n-${theme}-theme`]: theme
}"
@keydown="handlePanelKeyDown"
@focus="handlePanelFocus"
>
<div
v-if="active"
tabindex="0"
class="n-date-panel n-date-panel--datetime"
:class="{
[`n-${theme}-theme`]: theme
}"
@keydown="handlePanelKeyDown"
@focus="handlePanelFocus"
class="n-date-panel-input-wrapper"
>
<div
class="n-date-panel-input-wrapper"
>
<n-input
v-model="displayDateString"
:theme="theme"
:stateful="false"
size="small"
class="n-date-panel-date-input"
:class="{
'n-date-panel-date-input--invalid': isDateInvalid
}"
:placeholder="localeNamespace.selectDate"
@blur="handleDateInputBlur"
@input="handleDateInput"
/>
<n-time-picker
:show-icon="false"
:format="timeFormat"
:stateful="false"
:theme="theme"
size="small"
position-mode="absolute"
:detachable="false"
:value="value"
:placeholder="localeNamespace.selectTime"
stop-selector-bubble
:is-hour-disabled="isHourDisabled"
:is-minute-disabled="isMinuteDisabled"
:is-second-disabled="isSecondDisabled"
@change="handleTimePickerChange"
/>
</div>
<div class="n-date-panel-calendar">
<div class="n-date-panel-month">
<div
class="n-date-panel-month__fast-prev"
@click="prevYear"
>
<n-base-icon type="fast-backward" />
</div>
<div
class="n-date-panel-month__prev"
@click="prevMonth"
>
<n-base-icon type="backward" />
</div>
<div class="n-date-panel-month__month-year">
{{ calendarMonth }} {{ calendarYear }}
</div>
<div
class="n-date-panel-month__next"
@click="nextMonth"
>
<n-base-icon type="forward" />
</div>
<div
class="n-date-panel-month__fast-next"
@click="nextYear"
>
<n-base-icon type="fast-forward" />
</div>
</div>
<div class="n-date-panel-weekdays">
<div
v-for="weekday in weekdays"
:key="weekday"
class="n-date-panel-weekdays__day"
>
{{ weekday }}
</div>
</div>
<div class="n-date-panel-dates">
<div
v-for="(dateItem, i) in dateArray"
:key="i"
class="n-date-panel-date"
:class="{
'n-date-panel-date--current': dateItem.isCurrentDate,
'n-date-panel-date--selected': dateItem.isSelectedDate,
'n-date-panel-date--excluded': !dateItem.isDateOfDisplayMonth,
'n-date-panel-date--transition-disabled': noTransition,
'n-date-panel-date--disabled': isDateDisabled(dateItem.timestamp)
}"
@click="handleDateClick(dateItem)"
>
{{ dateItem.dateObject.date }}
</div>
<div
v-if="!(actions && actions.length)"
style="height: 8px; width: 100%;"
/>
</div>
</div>
<div
v-if="actions && actions.length"
class="n-date-panel-actions"
>
<n-button
v-if="actions.includes('clear')"
:theme="theme"
size="tiny"
@click="clearValue"
>
{{ localeNamespace.clear }}
</n-button>
<n-button
v-if="actions.includes('now')"
:theme="theme"
size="tiny"
@click="setSelectedDateTimeToNow"
>
{{ localeNamespace.now }}
</n-button>
<n-button
v-if="actions.includes('confirm')"
:theme="theme"
size="tiny"
type="primary"
:disabled="isDateTimeInvalid"
@click="handleConfirmClick"
>
{{ localeNamespace.confirm }}
</n-button>
</div>
<focus-detector @focus="handleFocusDetectorFocus" />
<n-input
v-model="displayDateString"
:theme="theme"
:stateful="false"
size="small"
class="n-date-panel-date-input"
:class="{
'n-date-panel-date-input--invalid': isDateInvalid
}"
:placeholder="localeNamespace.selectDate"
@blur="handleDateInputBlur"
@input="handleDateInput"
/>
<n-time-picker
:show-icon="false"
:format="timeFormat"
:stateful="false"
:theme="theme"
teleport-disabled
size="small"
:detachable="false"
:value="value"
:placeholder="localeNamespace.selectTime"
:is-hour-disabled="isHourDisabled"
:is-minute-disabled="isMinuteDisabled"
:is-second-disabled="isSecondDisabled"
@update:value="handleTimePickerChange"
/>
</div>
</transition>
<div class="n-date-panel-calendar">
<div class="n-date-panel-month">
<div
class="n-date-panel-month__fast-prev"
@click="prevYear"
>
<n-base-icon type="fast-backward" />
</div>
<div
class="n-date-panel-month__prev"
@click="prevMonth"
>
<n-base-icon type="backward" />
</div>
<div class="n-date-panel-month__month-year">
{{ calendarMonth }} {{ calendarYear }}
</div>
<div
class="n-date-panel-month__next"
@click="nextMonth"
>
<n-base-icon type="forward" />
</div>
<div
class="n-date-panel-month__fast-next"
@click="nextYear"
>
<n-base-icon type="fast-forward" />
</div>
</div>
<div class="n-date-panel-weekdays">
<div
v-for="weekday in weekdays"
:key="weekday"
class="n-date-panel-weekdays__day"
>
{{ weekday }}
</div>
</div>
<div class="n-date-panel-dates">
<div
v-for="(dateItem, i) in dateArray"
:key="i"
class="n-date-panel-date"
:class="{
'n-date-panel-date--current': dateItem.isCurrentDate,
'n-date-panel-date--selected': dateItem.isSelectedDate,
'n-date-panel-date--excluded': !dateItem.isDateOfDisplayMonth,
'n-date-panel-date--transition-disabled': noTransition,
'n-date-panel-date--disabled': isDateDisabled(dateItem.timestamp)
}"
@click="handleDateClick(dateItem)"
>
{{ dateItem.dateObject.date }}
</div>
<div
v-if="!(actions && actions.length)"
style="height: 8px; width: 100%;"
/>
</div>
</div>
<div
v-if="actions && actions.length"
class="n-date-panel-actions"
>
<n-button
v-if="actions.includes('clear')"
:theme="theme"
size="tiny"
@click="clearValue"
>
{{ localeNamespace.clear }}
</n-button>
<n-button
v-if="actions.includes('now')"
:theme="theme"
size="tiny"
@click="setSelectedDateTimeToNow"
>
{{ localeNamespace.now }}
</n-button>
<n-button
v-if="actions.includes('confirm')"
:theme="theme"
size="tiny"
type="primary"
:disabled="isDateTimeInvalid"
@click="handleConfirmClick"
>
{{ localeNamespace.confirm }}
</n-button>
</div>
<focus-detector @focus="handleFocusDetectorFocus" />
</div>
</template>
<script>
@ -184,7 +180,7 @@ export default {
this.initialValue = this.value
} else {
if (this.isTimeInvalid || this.isDateInvalid) {
this.$emit('input', this.initialValue)
this.doUpdateValue(this.initialValue)
}
}
}
@ -194,7 +190,7 @@ export default {
return startOfSecond(datetime)
},
handleTimePickerChange (value) {
this.$emit('input', value)
this.doUpdateValue(value)
}
}
}

View File

@ -1,264 +1,259 @@
<template>
<transition name="n-fade-in-scale-up-transition">
<div
tabindex="0"
class="n-date-panel n-date-panel--datetimerange"
:class="{
[`n-${theme}-theme`]: theme
}"
@click.capture="resetSelectingStatus"
@keydown="handlePanelKeyDown"
@focus="handlePanelFocus"
>
<div
v-if="active"
tabindex="0"
class="n-date-panel n-date-panel--datetimerange"
:class="{
[`n-${theme}-theme`]: theme
}"
@click.capture="resetSelectingStatus"
@keydown="handlePanelKeyDown"
@focus="handlePanelFocus"
class="n-date-panel-input-wrapper"
>
<div
class="n-date-panel-input-wrapper"
>
<n-input
v-model="startDateDisplayString"
:theme="theme"
size="small"
:stateful="false"
class="n-date-panel-date-input"
:class="{
'n-date-panel-date-input--invalid': isStartValueInvalid
}"
:placeholder="localeNamespace.selectDate"
@blur="handleStartDateInputBlur"
@input="handleStartDateInput"
<n-input
v-model="startDateDisplayString"
:theme="theme"
size="small"
:stateful="false"
class="n-date-panel-date-input"
:class="{
'n-date-panel-date-input--invalid': isStartValueInvalid
}"
:placeholder="localeNamespace.selectDate"
@blur="handleStartDateInputBlur"
@input="handleStartDateInput"
/>
<n-time-picker
size="small"
teleport-disabled
:show-icon="false"
:theme="theme"
:stateful="false"
:detachable="false"
:placeholder="localeNamespace.selectTime"
:format="timeFormat"
:value="startTimeValue"
:is-hour-disabled="isStartHourDisabled"
:is-minute-disabled="isStartMinuteDisabled"
:is-second-disabled="isStartSecondDisabled"
@update:value="handleStartTimePickerChange"
/>
<div class="n-date-panel-input-wrapper__arrow">
<n-base-icon
type="forward"
/>
<n-time-picker
size="small"
:show-icon="false"
:theme="theme"
:stateful="false"
:detachable="false"
:placeholder="localeNamespace.selectTime"
position-mode="absolute"
:format="timeFormat"
:value="startTimeValue"
:is-hour-disabled="isStartHourDisabled"
:is-minute-disabled="isStartMinuteDisabled"
:is-second-disabled="isStartSecondDisabled"
stop-selector-bubble
@change="handleStartTimePickerChange"
/>
<div class="n-date-panel-input-wrapper__arrow">
</div>
<n-input
v-model="endDateDisplayString"
:theme="theme"
:stateful="false"
size="small"
class="n-date-panel-date-input"
:class="{
'n-date-panel-date-input--invalid': isEndValueInvalid
}"
:placeholder="localeNamespace.selectDate"
@blur="handleEndDateInputBlur"
@input="handleEndDateInput"
/>
<n-time-picker
:show-icon="false"
:theme="theme"
size="small"
:stateful="false"
:detachable="false"
:format="timeFormat"
:placeholder="localeNamespace.selectTime"
position-mode="absolute"
:value="endTimeValue"
:is-hour-disabled="isEndHourDisabled"
:is-minute-disabled="isEndMinuteDisabled"
:is-second-disabled="isEndSecondDisabled"
@update:value="handleEndTimePickerChange"
/>
</div>
<div
ref="startDates"
class="n-date-panel-calendar n-date-panel-calendar--start"
>
<div class="n-date-panel-month">
<div
class="n-date-panel-month__fast-prev"
@click="startCalendarPrevYear"
>
<n-base-icon
type="fast-backward"
/>
</div>
<div
class="n-date-panel-month__prev"
@click="startCalendarPrevMonth"
>
<n-base-icon
type="backward"
/>
</div>
<div class="n-date-panel-month__month-year">
{{ startCalendarMonth }} {{ startCalendarYear }}
</div>
<div
class="n-date-panel-month__next"
@click="startCalendarNextMonth"
>
<n-base-icon
type="forward"
/>
</div>
<n-input
v-model="endDateDisplayString"
:theme="theme"
:stateful="false"
size="small"
class="n-date-panel-date-input"
:class="{
'n-date-panel-date-input--invalid': isEndValueInvalid
}"
:placeholder="localeNamespace.selectDate"
@blur="handleEndDateInputBlur"
@input="handleEndDateInput"
/>
<n-time-picker
:show-icon="false"
:theme="theme"
size="small"
:stateful="false"
:detachable="false"
:format="timeFormat"
:placeholder="localeNamespace.selectTime"
position-mode="absolute"
:value="endTimeValue"
:is-hour-disabled="isEndHourDisabled"
:is-minute-disabled="isEndMinuteDisabled"
:is-second-disabled="isEndSecondDisabled"
stop-selector-bubble
@change="handleEndTimePickerChange"
/>
</div>
<div
ref="startDates"
class="n-date-panel-calendar n-date-panel-calendar--start"
>
<div class="n-date-panel-month">
<div
class="n-date-panel-month__fast-prev"
@click="startCalendarPrevYear"
>
<n-base-icon
type="fast-backward"
/>
</div>
<div
class="n-date-panel-month__prev"
@click="startCalendarPrevMonth"
>
<n-base-icon
type="backward"
/>
</div>
<div class="n-date-panel-month__month-year">
{{ startCalendarMonth }} {{ startCalendarYear }}
</div>
<div
class="n-date-panel-month__next"
@click="startCalendarNextMonth"
>
<n-base-icon
type="forward"
/>
</div>
<div
class="n-date-panel-month__fast-next"
@click="startCalendarNextYear"
>
<n-base-icon
type="fast-forward"
/>
</div>
</div>
<div class="n-date-panel-weekdays">
<div
v-for="weekday in weekdays"
:key="weekday"
class="n-date-panel-weekdays__day"
>
{{ weekday }}
</div>
</div>
<div class="n-date-panel__divider" />
<div
class="n-date-panel-dates"
class="n-date-panel-month__fast-next"
@click="startCalendarNextYear"
>
<div
v-for="(dateItem, i) in startDateArray"
:key="i"
class="n-date-panel-date"
:class="{
'n-date-panel-date--current': dateItem.isCurrentDate,
'n-date-panel-date--selected': dateItem.isSelectedDate,
'n-date-panel-date--excluded': !dateItem.isDateOfDisplayMonth,
'n-date-panel-date--covered': dateItem.isInSpan,
'n-date-panel-date--transition-disabled': noTransition,
'n-date-panel-date--disabled': isCalendarDateDisabled(dateItem.timestamp)
}"
@click="handleDateClick(dateItem)"
@mouseenter="handleDateMouseEnter(dateItem)"
>
{{ dateItem.dateObject.date }}
</div>
</div>
</div>
<div class="n-date-panel__vertical-divider" />
<div
ref="endDates"
class="n-date-panel-calendar n-date-panel-calendar--end"
>
<div class="n-date-panel-month">
<div
class="n-date-panel-month__fast-prev"
@click="endCalendarPrevYear"
>
<n-base-icon
type="fast-backward"
/>
</div>
<div
class="n-date-panel-month__prev"
@click="endCalendarPrevMonth"
>
<n-base-icon
type="backward"
/>
</div>
<div class="n-date-panel-month__month-year">
{{ endCalendarMonth }} {{ endCalendarYear }}
</div>
<div
class="n-date-panel-month__next"
@click="endCalendarNextMonth"
>
<n-base-icon
type="forward"
/>
</div>
<div
class="n-date-panel-month__fast-next"
@click="endCalendarNextYear"
>
<n-base-icon
type="fast-forward"
/>
</div>
</div>
<div class="n-date-panel-weekdays">
<div
v-for="weekday in weekdays"
:key="weekday"
class="n-date-panel-weekdays__day"
>
{{ weekday }}
</div>
</div>
<div class="n-date-panel__divider" />
<div
class="n-date-panel-dates"
>
<div
v-for="(dateItem, i) in endDateArray"
:key="i"
class="n-date-panel-date"
:class="{
'n-date-panel-date--current': dateItem.isCurrentDate,
'n-date-panel-date--selected': dateItem.isSelectedDate,
'n-date-panel-date--excluded': !dateItem.isDateOfDisplayMonth,
'n-date-panel-date--covered': dateItem.isInSpan,
'n-date-panel-date--transition-disabled': noTransition,
'n-date-panel-date--disabled': isCalendarDateDisabled(dateItem.timestamp)
}"
@click="handleDateClick(dateItem)"
@mouseenter="handleDateMouseEnter(dateItem)"
>
{{ dateItem.dateObject.date }}
</div>
<div
v-if="!(actions && actions.length)"
style="height: 6px; width: 100%;"
<n-base-icon
type="fast-forward"
/>
</div>
</div>
<div
v-if="actions && actions.length"
class="n-date-panel-actions"
>
<n-button
v-if="actions.includes('clear')"
:theme="theme"
size="tiny"
@click="clearValue"
<div class="n-date-panel-weekdays">
<div
v-for="weekday in weekdays"
:key="weekday"
class="n-date-panel-weekdays__day"
>
{{ localeNamespace.clear }}
</n-button>
<n-button
v-if="actions.includes('confirm')"
:theme="theme"
:disabled="isRangeInvalid"
size="tiny"
type="primary"
@click="handleConfirmClick"
>
{{ localeNamespace.confirm }}
</n-button>
{{ weekday }}
</div>
</div>
<div class="n-date-panel__divider" />
<div
v-else
style="height: 12px"
/>
<focus-detector @focus="handleFocusDetectorFocus" />
class="n-date-panel-dates"
>
<div
v-for="(dateItem, i) in startDateArray"
:key="i"
class="n-date-panel-date"
:class="{
'n-date-panel-date--current': dateItem.isCurrentDate,
'n-date-panel-date--selected': dateItem.isSelectedDate,
'n-date-panel-date--excluded': !dateItem.isDateOfDisplayMonth,
'n-date-panel-date--covered': dateItem.isInSpan,
'n-date-panel-date--transition-disabled': noTransition,
'n-date-panel-date--disabled': isCalendarDateDisabled(dateItem.timestamp)
}"
@click="handleDateClick(dateItem)"
@mouseenter="handleDateMouseEnter(dateItem)"
>
{{ dateItem.dateObject.date }}
</div>
</div>
</div>
</transition>
<div class="n-date-panel__vertical-divider" />
<div
ref="endDates"
class="n-date-panel-calendar n-date-panel-calendar--end"
>
<div class="n-date-panel-month">
<div
class="n-date-panel-month__fast-prev"
@click="endCalendarPrevYear"
>
<n-base-icon
type="fast-backward"
/>
</div>
<div
class="n-date-panel-month__prev"
@click="endCalendarPrevMonth"
>
<n-base-icon
type="backward"
/>
</div>
<div class="n-date-panel-month__month-year">
{{ endCalendarMonth }} {{ endCalendarYear }}
</div>
<div
class="n-date-panel-month__next"
@click="endCalendarNextMonth"
>
<n-base-icon
type="forward"
/>
</div>
<div
class="n-date-panel-month__fast-next"
@click="endCalendarNextYear"
>
<n-base-icon
type="fast-forward"
/>
</div>
</div>
<div class="n-date-panel-weekdays">
<div
v-for="weekday in weekdays"
:key="weekday"
class="n-date-panel-weekdays__day"
>
{{ weekday }}
</div>
</div>
<div class="n-date-panel__divider" />
<div
class="n-date-panel-dates"
>
<div
v-for="(dateItem, i) in endDateArray"
:key="i"
class="n-date-panel-date"
:class="{
'n-date-panel-date--current': dateItem.isCurrentDate,
'n-date-panel-date--selected': dateItem.isSelectedDate,
'n-date-panel-date--excluded': !dateItem.isDateOfDisplayMonth,
'n-date-panel-date--covered': dateItem.isInSpan,
'n-date-panel-date--transition-disabled': noTransition,
'n-date-panel-date--disabled': isCalendarDateDisabled(dateItem.timestamp)
}"
@click="handleDateClick(dateItem)"
@mouseenter="handleDateMouseEnter(dateItem)"
>
{{ dateItem.dateObject.date }}
</div>
<div
v-if="!(actions && actions.length)"
style="height: 6px; width: 100%;"
/>
</div>
</div>
<div
v-if="actions && actions.length"
class="n-date-panel-actions"
>
<n-button
v-if="actions.includes('clear')"
:theme="theme"
size="tiny"
@click="clearValue"
>
{{ localeNamespace.clear }}
</n-button>
<n-button
v-if="actions.includes('confirm')"
:theme="theme"
:disabled="isRangeInvalid"
size="tiny"
type="primary"
@click="handleConfirmClick"
>
{{ localeNamespace.confirm }}
</n-button>
</div>
<div
v-else
style="height: 12px"
/>
<focus-detector @focus="handleFocusDetectorFocus" />
</div>
</template>
<script>
@ -313,7 +308,7 @@ export default {
this.memorizedValue = this.value
this.syncCalendarTimeWithValue(this.value)
} else if (this.isRangeInvalid) {
this.$emit('input', this.memorizedValue)
this.doUpdateValue(this.memorizedValue)
}
},
valueAsDateArray (newValue) {

View File

@ -15,14 +15,6 @@ import commonCalendarMixin from './commonCalendarMixin'
export default {
mixins: [commonCalendarMixin],
props: {
theme: {
type: String,
default: null
},
active: {
type: Boolean,
default: true
},
value: {
validator (value) {
if (value === null) return true
@ -281,9 +273,6 @@ export default {
this.isSelecting = false
}
},
handleClickOutside () {
this.closeCalendar()
},
syncCalendarTimeWithValue (value) {
if (value === null) return
const [startMoment, endMoment] = value
@ -326,13 +315,13 @@ export default {
if (this.isRangeInvalid) {
return
}
this.$emit('confirm')
this.doConfirm()
this.closeCalendar()
},
closeCalendar () {
this.isSelecting = false
if (this.active) {
this.$emit('close')
this.doClose()
}
},
changeStartDateTime (time) {
@ -340,9 +329,9 @@ export default {
time = getTime(time)
}
if (this.value === null) {
this.$emit('input', [time, time])
this.doUpdateValue([time, time])
} else {
this.$emit('input', [time, Math.max(this.value[1], time)])
this.doUpdateValue([time, Math.max(this.value[1], time)])
}
},
changeEndDateTime (time) {
@ -350,9 +339,9 @@ export default {
time = getTime(time)
}
if (this.value === null) {
this.$emit('input', [time, time])
this.doUpdateValue([time, time])
} else {
this.$emit('input', [Math.min(this.value[0], time), time])
this.doUpdateValue([Math.min(this.value[0], time), time])
}
},
changeStartEndTime (startTime, endTime) {
@ -363,7 +352,7 @@ export default {
if (typeof endTime !== 'number') {
endTime = getTime(endTime)
}
this.$emit('input', [startTime, endTime])
this.doUpdateValue([startTime, endTime])
},
/** change calendar time */
adjustCalendarTimes (byStartCalendarTime) {

View File

@ -16,14 +16,6 @@ import { dateArray, strictParse } from '../../../_utils/component/datePicker'
export default {
mixins: [commonCalendarMixin],
props: {
theme: {
type: String,
default: null
},
active: {
type: Boolean,
default: true
},
value: {
type: Number,
required: false,
@ -146,21 +138,18 @@ export default {
this.NDatePicker.setInvalidStatus(this.isDateTimeInvalid)
},
methods: {
handleClickOutside () {
this.closeCalendar()
},
handleDateInput (value) {
const date = strictParse(value, this.dateFormat, new Date())
if (isValid(date)) {
if (!this.valueAsDateTime) {
this.$emit('input', getTime(this.adjustValue(new Date())))
this.doUpdateValue(getTime(this.adjustValue(new Date())))
} else {
const newDateTime = set(this.valueAsDateTime, {
year: getYear(date),
month: getMonth(date),
date: getDate(date)
})
this.$emit('input', getTime(this.adjustValue(newDateTime)))
this.doUpdateValue(getTime(this.adjustValue(newDateTime)))
}
}
},
@ -168,25 +157,25 @@ export default {
const date = strictParse(this.displayDateString, this.dateFormat, new Date())
if (isValid(date)) {
if (!this.valueAsDateTime) {
this.$emit('input', getTime(this.adjustValue(new Date())))
this.doUpdateValue(getTime(this.adjustValue(new Date())))
} else {
const newDateTime = set(this.valueAsDateTime, {
year: getYear(date),
month: getMonth(date),
date: getDate(date)
})
this.$emit('input', getTime(this.adjustValue(newDateTime)))
this.doUpdateValue(getTime(this.adjustValue(newDateTime)))
}
} else {
this.refreshDisplayDateString()
}
},
clearSelectedDateTime () {
this.$emit('input', null)
this.doUpdateValue(null)
this.displayDateString = ''
},
setSelectedDateTimeToNow () {
this.$emit('input', getTime(this.adjustValue(new Date())))
this.doUpdateValue(getTime(this.adjustValue(new Date())))
this.calendarDateTime = new Date()
},
handleDateClick (dateItem) {
@ -199,7 +188,7 @@ export default {
}
newSelectedDateTime = set(newSelectedDateTime, dateItem.dateObject)
this.selectedDate = dateItem.dateObject
this.$emit('input', getTime(this.adjustValue(newSelectedDateTime)))
this.doUpdateValue(getTime(this.adjustValue(newSelectedDateTime)))
},
/**
* If not selected, display nothing,
@ -219,12 +208,12 @@ export default {
if (this.isDateInvalid || this.isTimeInvalid) {
return
}
this.$emit('confirm')
this.doConfirm()
this.closeCalendar()
},
closeCalendar () {
if (this.active) {
this.$emit('close')
this.doClose()
}
},
nextYear () {

View File

@ -417,11 +417,11 @@ export default {
if (onFocus) call(onFocus, e)
__triggerFormFocus()
},
doClear () {
doClear (...args) {
const {
onClear
} = this
if (onClear) call(onClear)
if (onClear) call(onClear, ...args)
},
doInputBlur (e) {
const {

View File

@ -117,10 +117,6 @@ export default {
themeable,
usecssr(styles)
],
model: {
prop: 'active-name',
event: 'active-name-change'
},
props: {
value: {
type: String || Number,

View File

@ -1,8 +1,8 @@
/* istanbul ignore file */
import Scaffold from './src/TimePicker.vue'
import TimePicker from './src/TimePicker.vue'
Scaffold.install = function (app, naive) {
app.component(naive.componentPrefix + Scaffold.name, Scaffold)
TimePicker.install = function (app, naive) {
app.component(naive.componentPrefix + TimePicker.name, TimePicker)
}
export default Scaffold
export default TimePicker

View File

@ -35,8 +35,9 @@
</template>
</n-input>
<n-lazy-teleport
to="body"
:to="to"
:show="active"
:disabled="teleportDisabled"
>
<div
ref="offsetContainer"
@ -326,10 +327,18 @@ export default {
// deprecated
onChange: {
validator () {
if (__DEV__) warn('time-picker', '')
if (__DEV__) warn('time-picker', '`on-change` is deprecated, please use `on-update:value` instead.')
return true
},
default: undefined
},
to: {
type: [String, Object],
default: undefined
},
teleportDisabled: {
type: Boolean,
default: false
}
},
setup () {

View File

@ -66,7 +66,14 @@ placeable 进行了大调整
- remove
- `$NConfirm`, `$NModal` => `inject.dialog`
- [ ] data-table
- [ ] date-picker
- [x] date-picker
- break
- `v-model` => `v-model:value`
- deprecate
- `on-change` => `on-update:value`
- bug
- time-picker close animation
- invalid time (TODO: hoist invalid)
- [x] descriptions
- [x] divider
- [x] drawer