Created
August 1, 2017 10:28
-
-
Save willishq/7c33f7bcfb081c8b8dad4e45d4122768 to your computer and use it in GitHub Desktop.
VueJS Datepicker component
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div> | |
<label :for="name" :class="{ | |
'label-error': errors.has(name), | |
}">{{ label }}</label> | |
<div class="datepicker-selects"> | |
<select v-if="showDay" v-model="day" :class="{ | |
'input-error': !isValid('day') || errors.has(name), | |
}" :disabled="!isInitialised()"> | |
<option :value="null" disabled :selected="day === null">Day</option> | |
<option v-for="(day, index) in days" :value="index + 1" :disabled="dayIsDisabled(index)"> | |
{{ index + 1 }} | |
</option> | |
</select> | |
<select v-model="month" :class="{ | |
'input-error': !isValid('month') || errors.has(name), | |
}" :disabled="!isInitialised()"> | |
<option :value="null" disabled :selected="month === null">Month</option> | |
<option v-for="(month, index) in months" :value="index" :disabled="monthIsDisabled(index)"> | |
{{ month }} | |
</option> | |
</select> | |
<select v-model="year" :class="{ | |
'input-error': !isValid('year') || errors.has(name), | |
}" :disabled="!isInitialised()"> | |
<option :value="null" disabled :selected="year === null">Year</option> | |
<option v-for="(year, index) in years" :value="year"> | |
{{ year }} | |
</option> | |
</select> | |
</div> | |
<span class="form-error is-visible" v-show="errors.has(name)" v-for="error in errors.collect(name)"> | |
{{ error }} | |
</span> | |
</div> | |
</template> | |
<script> | |
import times from 'lodash/fp/times'; | |
import moment from 'moment'; | |
export default { | |
props: { | |
showDay: { | |
type: Boolean, | |
default: true, | |
}, | |
name: { | |
required: true, | |
type: String, | |
}, | |
label: { | |
required: true, | |
type: String, | |
}, | |
maxDate: { | |
type: Object, | |
}, | |
minDate: { | |
type: Object, | |
}, | |
value: { | |
required: true, | |
}, | |
errors: { | |
required: false, | |
} | |
}, | |
data() { | |
return { | |
year: this.value.year, | |
month: this.value.month, | |
day: this.value.day, | |
months: times(i => moment().month(i).format('MMM'))(12), | |
}; | |
}, | |
methods: { | |
isFilled() { | |
return this.date.year && this.date.month && (this.showDay ? this.date.day : true); | |
}, | |
isValid() { | |
if (this.isFilled()) { | |
return moment(this.date).isBetween(this.minDateMoment, this.maxDateMoment, 'day', '[]'); | |
} | |
return true; | |
}, | |
dayIsDisabled(i) { | |
if (this.isInitialised() && this.showDay) { | |
let selected = moment([this.year, this.month, i + 1]); | |
return !selected.isBetween(this.minDateMoment, this.maxDateMoment, 'day', '[]'); | |
} | |
return true; | |
}, | |
monthIsDisabled(i) { | |
if (this.isInitialised()) { | |
let selected = moment([this.year, i, 1]); | |
return !selected.isBetween(this.minDateMoment, this.maxDateMoment, 'month', '[]'); | |
} | |
return true; | |
}, | |
isInitialised() { | |
return this.minDateMoment && this.maxDateMoment; | |
} | |
}, | |
computed: { | |
maxDateMoment() { | |
if (this.maxDate.month !== null) { | |
return moment([this.maxDate.year, this.maxDate.month, this.maxDate.day || 1]); | |
} | |
return null; | |
}, | |
minDateMoment() { | |
if (this.minDate.month !== null) { | |
return moment([this.minDate.year, this.minDate.month, this.minDate.day || 1]); | |
} | |
return null; | |
}, | |
yearCount() { | |
return this.isInitialised() ? (this.maxDateMoment.year() - this.minDateMoment.year()) + 1 : 0; | |
}, | |
years() { | |
if (this.isInitialised()) { | |
let min = this.minDateMoment; | |
let years = times(i => min.clone().add(i, 'years').year())(this.yearCount); | |
if (this.maxDateMoment.isSameOrBefore(moment())) { | |
return years.reverse(); | |
} | |
return years; | |
} | |
return []; | |
}, | |
days() { | |
return times((d) => { | |
return d + 1; | |
})(this.maxDay); | |
}, | |
maxDay() { | |
return moment([this.year || moment().year(), this.month]).daysInMonth() || 31; | |
}, | |
date() { | |
let date = {year: this.year, month: this.month}; | |
if (this.showDay) { | |
date.day = this.day; | |
} | |
return date; | |
} | |
}, | |
watch: { | |
date() { | |
if (this.isFilled()) { | |
this.$emit('input', this.date); | |
} | |
} | |
} | |
}; | |
</script> | |
<style lang="scss"> | |
.datepicker-selects { | |
select { | |
width: auto; | |
display: inline-block; | |
} | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment