Skip to content

Instantly share code, notes, and snippets.

@willishq
Created August 1, 2017 10:28
Show Gist options
  • Save willishq/7c33f7bcfb081c8b8dad4e45d4122768 to your computer and use it in GitHub Desktop.
Save willishq/7c33f7bcfb081c8b8dad4e45d4122768 to your computer and use it in GitHub Desktop.
VueJS Datepicker component
<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