feat(date-picker): v-model and some robustness enhance for it.

This commit is contained in:
07akioni 2019-07-08 16:15:23 +08:00
parent e0da00b631
commit d20e707244
4 changed files with 250 additions and 103 deletions

View File

@ -14,13 +14,66 @@
Basic Usage Basic Usage
</div> </div>
<div class="n-doc-section__view"> <div class="n-doc-section__view">
<n-input
v-model="dateTimeTimestamp"
placeholder="dateTimeTimestamp"
type="text"
/>
<div style="width: 100%;"> <div style="width: 100%;">
timestamp: {{ timestamp }} dateTimeTimestamp: {{ dateTimeTimestamp }}
</div> </div>
<n-date-picker v-model="timestamp" /> <n-with-padding :padding-top="12" />
<n-input
v-model="dateTimestamp"
placeholder="dateTimestamp"
type="text"
/>
<div style="width: 100%;">
dateTimeStamp: {{ dateTimestamp }}
</div>
<n-with-padding :padding-top="12" />
<n-date-picker
v-model="dateTimeTimestamp"
type="datetime"
@change="onDateTimeChange"
/>
<n-with-padding :padding-top="12" />
<n-date-picker
v-model="dateTimestamp"
type="date"
@change="onDateChange"
/>
</div> </div>
<div class="n-doc-section__source"> <div class="n-doc-section__source">
<textarea>scaffold</textarea> <textarea><n-date-picker
v-model="dateTimeTimestamp"
type="datetime"
@change="onDateTimeChange"
/>
<n-date-picker
v-model="dateTimestamp"
type="date"
@change="onDateChange"
/>
<script>
export default {
data () {
return {
dateTimeTimestamp: 666666666,
dateTimestamp: null
}
},
methods: {
onDateTimeChange (timestamp, dateTimeString) {
this.$NMessage.success(`${timestamp}, ${dateTimeString}`)
},
onDateChange (timestamp, dateString) {
this.$NMessage.success(`${timestamp}, ${dateString}`)
}
}
}
</script>
</textarea>
</div> </div>
</div> </div>
</div> </div>
@ -33,10 +86,17 @@ export default {
mixins: [docCodeEditorMixin], mixins: [docCodeEditorMixin],
data () { data () {
return { return {
timestamp: 0 dateTimeTimestamp: 666666666,
dateTimestamp: null
} }
}, },
methods: { methods: {
onDateTimeChange (timestamp, dateTimeString) {
this.$NMessage.success(`${timestamp}, ${dateTimeString}`)
},
onDateChange (timestamp, dateString) {
this.$NMessage.success(`${timestamp}, ${dateString}`)
}
} }
} }
</script> </script>

View File

@ -10,7 +10,7 @@
<input <input
v-model="displayDateTimeString" v-model="displayDateTimeString"
class="n-date-picker__input" class="n-date-picker__input"
placeholder="Select date and time" :placeholder="placeholder"
@click="openCalendar" @click="openCalendar"
@focus="openCalendar" @focus="openCalendar"
@blur="handleDateTimeInputBlur" @blur="handleDateTimeInputBlur"
@ -24,14 +24,19 @@
</div> </div>
<transition name="n-date-picker-calendar--transition"> <transition name="n-date-picker-calendar--transition">
<div <div
v-if="showCalendar" v-if="showCalendar || debug"
class="n-date-picker-calendar" class="n-date-picker-calendar"
> >
<div class="n-date-picker-calendar__date-time-input-wrapper"> <div
v-if="type==='datetime'"
class="n-date-picker-calendar__date-time-input-wrapper"
>
<input <input
v-model="displayDateString" v-model="displayDateString"
class="n-date-picker-calendar__date-input" class="n-date-picker-calendar__date-input"
placeholder="Select date" placeholder="Select date"
@blur="handleDateInputBlur"
@input="handleDateInput"
> >
<div <div
ref="timeSelector" ref="timeSelector"
@ -43,6 +48,7 @@
placeholder="Select time" placeholder="Select time"
@click="openTimeSelector" @click="openTimeSelector"
@input="handleTimeInput" @input="handleTimeInput"
@blur="handleTimeInputBlur"
> >
<transition name="n-date-picker-time-selector--transition"> <transition name="n-date-picker-time-selector--transition">
<div <div
@ -115,6 +121,10 @@
</transition> </transition>
</div> </div>
</div> </div>
<div
v-else
style="width: 100%; height: 12px;"
/>
<div class="n-date-picker-calendar__month-modifier"> <div class="n-date-picker-calendar__month-modifier">
<div <div
class="n-date-picker-calendar__fast-prev" class="n-date-picker-calendar__fast-prev"
@ -139,7 +149,7 @@
/> />
</div> </div>
<div class="n-date-picker-calendar__month-year"> <div class="n-date-picker-calendar__month-year">
{{ calendarTime.startOf('month').format('MMMM') }} {{ calendarTime.year() }} {{ calendarDateTime.format('MMMM') }} {{ calendarDateTime.year() }}
</div> </div>
<div <div
class="n-date-picker-calendar__next" class="n-date-picker-calendar__next"
@ -176,7 +186,7 @@
<div class="n-date-picker-calendar__divider" /> <div class="n-date-picker-calendar__divider" />
<div class="n-date-picker-calendar__dates"> <div class="n-date-picker-calendar__dates">
<div <div
v-for="dateItem in dateArray(calendarTime, selectedDateTime, currentTime)" v-for="dateItem in dateArray(calendarDateTime, computedSelectedDateTime, currentDateTime)"
:key="dateItem.timestamp" :key="dateItem.timestamp"
class="n-date-picker-calendar__date" class="n-date-picker-calendar__date"
:class="{ :class="{
@ -218,7 +228,20 @@ import { dateArray, setDate } from './utils'
import NIcon from '../../Icon' import NIcon from '../../Icon'
import NButton from '../../Button' import NButton from '../../Button'
const dateFormat = 'YYYY-MM-DD HH:mm:ss' const DATE_FORMAT = {
date: 'YYYY-MM-DD',
datetime: 'YYYY-MM-DD HH:mm:ss'
}
const PLACEHOLDER = {
date: 'Select date',
datetime: 'Select date and time'
}
const TIME_CONST = {
weekdays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
hours: ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'],
minutes: ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59'],
seconds: ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59']
}
export default { export default {
name: 'NDatePicker', name: 'NDatePicker',
@ -232,12 +255,24 @@ export default {
}, },
props: { props: {
value: { value: {
type: Number, type: [Number, String],
required: true required: false,
default: null
}, },
size: { size: {
type: String, type: String,
default: 'default' default: 'default'
},
/**
* type can be 'date', 'datetime'
*/
type: {
type: String,
default: 'date'
},
debug: {
type: Boolean,
default: false
} }
}, },
data () { data () {
@ -245,61 +280,116 @@ export default {
displayDateTimeString: '', displayDateTimeString: '',
displayDateString: '', displayDateString: '',
displayTimeString: '', displayTimeString: '',
calendarTime: moment(), calendarDateTime: moment(),
selectedDateTime: null, currentDateTime: moment(),
showCalendar: false, showCalendar: false,
showTimeSelector: false, showTimeSelector: false,
calendar: [], calendar: [],
currentTime: moment(), ...TIME_CONST
weekdays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
hours: ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'],
minutes: ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59'],
seconds: ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59']
} }
}, },
computed: { computed: {
placeholder () {
return PLACEHOLDER[this.type]
},
format () {
return DATE_FORMAT[this.type]
},
computedHour () { computedHour () {
if (this.selectedDateTime) return this.selectedDateTime.format('HH') if (this.computedSelectedDateTime) return this.computedSelectedDateTime.format('HH')
else return null else return null
}, },
computedMinute () { computedMinute () {
if (this.selectedDateTime) return this.selectedDateTime.format('mm') if (this.computedSelectedDateTime) return this.computedSelectedDateTime.format('mm')
else return null else return null
}, },
computedSecond () { computedSecond () {
if (this.selectedDateTime) return this.selectedDateTime.format('ss') if (this.computedSelectedDateTime) return this.computedSelectedDateTime.format('ss')
else return null else return null
},
/**
* If value is valid return null.
* If value is not valid, return moment(value)
*/
computedSelectedDateTime () {
if (this.value === null || this.value === undefined) return null
const newSelectedDateTime = moment(Number(this.value))
if (newSelectedDateTime.isValid()) {
return newSelectedDateTime
} else {
return null
}
} }
}, },
watch: { watch: {
selectedDateTime (newValue) { /**
this.$emit('change', newValue.valueOf()) * If new value is valid, set calendarTime and refresh display strings.
* If new value is invalid, do nothing.
*/
value (newValue) {
const newSelectedDateTime = moment(Number(newValue))
if (newSelectedDateTime.isValid()) {
this.calendarDateTime = moment(newSelectedDateTime)
this.refreshSelectedDateTimeString()
} }
}
},
created () {
this.refreshSelectedDateTimeString()
}, },
methods: { methods: {
dateArray, dateArray,
/**
* If new datetime is null or undefined, emit null to value.
* Else adjust new datetime by props.type and emit it to value.
*/
setValue (newSelectedDateTime) {
if (newSelectedDateTime === null || newSelectedDateTime === undefined) {
this.$emit('change', null)
return
}
if (newSelectedDateTime.isValid()) {
const adjustedDateTime = this.adjustDateTimeAccrodingToType(newSelectedDateTime)
if (this.computedSelectedDateTime === null || adjustedDateTime.valueOf() !== this.computedSelectedDateTime.valueOf()) {
this.$emit('change', adjustedDateTime.valueOf(), adjustedDateTime.format(this.format))
}
}
},
adjustDateTimeAccrodingToType (datetime) {
if (this.type === 'datetime') {
return moment(datetime).startOf('second')
} else {
return moment(datetime).startOf('date').hour(0)
}
},
justifySelectedDateTimeAfterChangeTimeString () { justifySelectedDateTimeAfterChangeTimeString () {
if (this.selectedDateTime === null) { if (this.computedSelectedDateTime === null) {
// case here is impossible for now, because you can't clear time // case here is impossible for now, because you can't clear time for now
} else { } else {
const newDisplayDateTimeString = this.displayDateString + ' ' + this.displayTimeString const newDisplayDateTimeString = this.displayDateString + ' ' + this.displayTimeString
const newSelectedDateTime = moment(newDisplayDateTimeString, 'YYYY-MM-DD HH:mm:ss', true) const newSelectedDateTime = moment(newDisplayDateTimeString, this.format, true)
if (newSelectedDateTime.isValid()) { this.setValue(newSelectedDateTime)
this.selectedDateTime = newSelectedDateTime
} }
}
this.refreshSelectedDateTime()
}, },
handleTimeInput (e) { handleTimeInput (e) {
const newDisplayDateTimeString = this.displayDateString + ' ' + e.target.value const newDisplayDateTimeString = this.displayDateString + ' ' + e.target.value
const newSelectedDateTime = moment(newDisplayDateTimeString, 'YYYY-MM-DD HH:mm:ss', true) const newSelectedDateTime = moment(newDisplayDateTimeString, this.format, true)
if (newSelectedDateTime.isValid()) { this.setValue(newSelectedDateTime)
this.selectedDateTime = newSelectedDateTime },
this.refreshSelectedDateTime() handleDateInput (e) {
} const newDisplayDateTimeString = e.target.value + ' ' + this.displayTimeString
const newSelectedDateTime = moment(newDisplayDateTimeString, this.format, true)
this.setValue(newSelectedDateTime)
},
handleTimeInputBlur () {
this.refreshSelectedDateTimeString()
},
handleDateInputBlur () {
this.refreshSelectedDateTimeString()
}, },
handleTimeConfirmClick () { handleTimeConfirmClick () {
this.justifySelectedDateTimeAfterChangeTimeString() this.justifySelectedDateTimeAfterChangeTimeString()
this.refreshSelectedDateTimeString()
this.closeTimeSelector() this.closeTimeSelector()
}, },
handleTimeCancelClick () { handleTimeCancelClick () {
@ -339,84 +429,93 @@ export default {
} }
}, },
openTimeSelector () { openTimeSelector () {
if (this.selectedDateTime === null) { if (this.computedSelectedDateTime === null) {
this.selectedDateTime = moment() this.setValue(moment())
this.refreshSelectedDateTime()
} }
this.showTimeSelector = true this.showTimeSelector = true
}, },
closeTimeSelector () { closeTimeSelector () {
this.showTimeSelector = false this.showTimeSelector = false
}, },
/**
* To close timeSelector
*/
handleCalendarClick (e) { handleCalendarClick (e) {
if (!this.$refs.timeSelector) return
if (!this.$refs.timeSelector.contains(e.target) && this.$refs.timeSelector !== e.target) { if (!this.$refs.timeSelector.contains(e.target) && this.$refs.timeSelector !== e.target) {
this.closeTimeSelector() this.closeTimeSelector()
} }
}, },
nativeCloseCalendar (e) { nativeCloseCalendar (e) {
if (e.target.contains(this.$refs.datePicker) && e.target !== this.$refs.datePicker) { if (!this.$refs.datePicker.contains(e.target)) {
this.closeCalendar() this.closeCalendar()
} }
}, },
clear () { clearSelectedDateTime () {
this.selectedDateTime = null this.setValue(null)
this.displayDateTimeString = '' this.displayDateTimeString = ''
this.displayDateString = '' this.displayDateString = ''
this.displayTimeString = '' this.displayTimeString = ''
}, },
setSelectedDateTimeToNow () { setSelectedDateTimeToNow () {
this.selectedDateTime = moment() this.setValue(moment())
this.calendarTime = moment() this.calendarDateTime = moment()
this.refreshSelectedDateTime()
}, },
handleDateClick (dateItem) { handleDateClick (dateItem) {
if (!this.selectedDateTime) { let newSelectedDateTime = moment()
this.selectedDateTime = moment() if (this.computedSelectedDateTime !== null) {
this.selectedDateTime = setDate(this.selectedDateTime, dateItem) newSelectedDateTime = moment(this.computedSelectedDateTime)
} else {
this.selectedDateTime = setDate(this.selectedDateTime, dateItem)
} }
this.calendarTime = moment(this.selectedDateTime) newSelectedDateTime = setDate(newSelectedDateTime, dateItem)
this.setValue(newSelectedDateTime)
this.refreshSelectedDateTime()
}, },
refreshSelectedDateTime () { /**
if (this.selectedDateTime === null) { * If not selected, display nothing,
* else update datetime related string
*/
refreshSelectedDateTimeString () {
if (this.computedSelectedDateTime === null) {
this.displayDateTimeString = '' this.displayDateTimeString = ''
return return
} }
this.displayDateTimeString = this.selectedDateTime.format('YYYY-MM-DD HH:mm:ss') this.displayDateTimeString = this.computedSelectedDateTime.format(this.format)
this.displayDateString = this.selectedDateTime.format('YYYY-MM-DD') this.displayDateString = this.computedSelectedDateTime.format('YYYY-MM-DD')
this.displayTimeString = this.selectedDateTime.format('HH:mm:ss') this.displayTimeString = this.computedSelectedDateTime.format('HH:mm:ss')
}, },
/**
* If new time is invalid, do nothing.
* If valid, update.
*/
handleDateTimeInputEnter () { handleDateTimeInputEnter () {
const newSelectedDateTime = moment(this.displayDateTimeString, 'YYYY-MM-DD HH:mm:ss', true) const newSelectedDateTime = moment(this.displayDateTimeString, this.format, true)
if (newSelectedDateTime.isValid()) { this.setValue(newSelectedDateTime)
this.selectedDateTime = newSelectedDateTime
this.calendarTime = moment(newSelectedDateTime)
this.refreshSelectedDateTime()
}
}, },
/**
* If new SelectedDateTime is valid, update `selectedDateTime` and `calendarTime`
* Whatever happened, refresh selectedDateTime
*/
handleDateTimeInputBlur () { handleDateTimeInputBlur () {
const newSelectedDateTime = moment(this.displayDateTimeString, dateFormat, true) const newSelectedDateTime = moment(this.displayDateTimeString, this.format, true)
if (newSelectedDateTime.isValid()) { this.setValue(newSelectedDateTime)
this.selectedDateTime = newSelectedDateTime /**
this.calendarTime = moment(newSelectedDateTime) * If newSelectedDateTime is invalid, display string need to be restored
} */
this.refreshSelectedDateTime() this.refreshSelectedDateTimeString()
}, },
handleDateInputAndTimeInputConfirmClick () { handleDateInputAndTimeInputConfirmClick () {
const newDisplayDateTimeString = `${this.displayDateString.trim()} ${this.displayTimeString.trim()}` const newDisplayDateTimeString = `${this.displayDateString.trim()} ${this.displayTimeString.trim()}`
const newSelectedDateTime = moment(newDisplayDateTimeString, dateFormat, true) const newSelectedDateTime = moment(newDisplayDateTimeString, this.format, true)
if (newSelectedDateTime.isValid()) { if (this.computedSelectedDateTime === null) {
this.selectedDateTime = newSelectedDateTime this.setValue(moment())
} else if (this.selectedDateTime === null) { } else {
this.selectedDateTime = moment() this.setValue(newSelectedDateTime)
this.refreshSelectedDateTimeString()
} }
this.refreshSelectedDateTime()
this.closeCalendar() this.closeCalendar()
}, },
/**
* Calendar view related methods
*/
openCalendar () { openCalendar () {
this.showCalendar = true this.showCalendar = true
document.body.addEventListener('click', this.nativeCloseCalendar) document.body.addEventListener('click', this.nativeCloseCalendar)
@ -430,39 +529,21 @@ export default {
}, },
nextYear () { nextYear () {
this.calendarTime.add(1, 'year') this.calendarDateTime.add(1, 'year')
this.$forceUpdate() this.$forceUpdate()
}, },
prevYear () { prevYear () {
this.calendarTime.subtract(1, 'year') this.calendarDateTime.subtract(1, 'year')
this.$forceUpdate() this.$forceUpdate()
}, },
nextMonth () { nextMonth () {
this.calendarTime.add(1, 'month') this.calendarDateTime.add(1, 'month')
this.$forceUpdate() this.$forceUpdate()
}, },
prevMonth () { prevMonth () {
this.calendarTime.subtract(1, 'month') this.calendarDateTime.subtract(1, 'month')
this.$forceUpdate() this.$forceUpdate()
} }
} }
} }
</script> </script>
<style lang="scss" scoped>
.date {
text-align: center;
width: 36px;
opacity: .6;
&.n-date-picker-calendar__date--current {
color: blue;
}
&.n-date-picker-calendar__date--selected {
background-color: red;
}
cursor: pointer;
&.n-date-picker-calendar__date--in-display-month, &.n-date-picker-calendar__date--day {
opacity: 1;
}
}
</style>

View File

@ -1,7 +1,13 @@
import moment from 'moment' import moment from 'moment'
/**
* change date of `time` accroding to `dateItem`
* keep time of `time`
* return a new Moment Object according to time
* @param {Moment} time
* @param {Object} dateItem
*/
function setDate (time, dateItem) { function setDate (time, dateItem) {
console.log(time, dateItem)
time.year(dateItem.year) time.year(dateItem.year)
time.month(dateItem.month) time.month(dateItem.month)
time.date(dateItem.date) time.date(dateItem.date)

View File

@ -77,7 +77,7 @@ export default {
default: '' default: ''
}, },
value: { value: {
type: String, type: [String, Number],
default: '' default: ''
}, },
disabled: { disabled: {