UNPKG

vuetify

Version:

Vue.js 2 Semantic Component Framework

402 lines (376 loc) 12 kB
// Components import VBtn from '../VBtn' import VCard from '../VCard' import VIcon from '../VIcon' import VDatePickerTitle from './VDatePickerTitle' import VDatePickerHeader from './VDatePickerHeader' import VDatePickerDateTable from './VDatePickerDateTable' import VDatePickerMonthTable from './VDatePickerMonthTable' import VDatePickerYears from './VDatePickerYears' // Mixins import Picker from '../../mixins/picker' // Utils import { pad, createNativeLocaleFormatter } from './util' import isValueAllowed from '../../util/isValueAllowed' export default { name: 'v-date-picker', components: { VBtn, VCard, VIcon, VDatePickerTitle, VDatePickerHeader, VDatePickerDateTable, VDatePickerMonthTable, VDatePickerYears }, mixins: [Picker], data () { const now = new Date() return { activePicker: this.type.toUpperCase(), defaultColor: 'accent', isReversing: false, now, originalDate: this.value, // tableDate is a string in 'YYYY' / 'YYYY-M' format (leading zero for month is not required) tableDate: this.type === 'month' ? `${now.getFullYear()}` : `${now.getFullYear()}-${now.getMonth() + 1}` } }, props: { allowedDates: { type: [Array, Object, Function], default: () => null }, autosave: Boolean, appendIcon: { type: String, default: 'chevron_right' }, // Function formatting the day in date picker table dayFormat: { type: Function, default: null }, events: { type: [Array, Object, Function], default: () => null }, eventColor: { type: [String, Function, Object], default: 'warning' }, firstDayOfWeek: { type: [String, Number], default: 0 }, // Function formatting the tableDate in the day/month table header headerDateFormat: { type: Function, default: null }, locale: { type: String, default: 'en-us' }, // Function formatting month in the months table monthFormat: { type: Function, default: null }, prependIcon: { type: String, default: 'chevron_left' }, readonly: Boolean, scrollable: Boolean, showCurrent: { type: [Boolean, String], default: true }, // Function formatting currently selected date in the picker title titleDateFormat: { type: Function, default: null }, type: { type: String, default: 'date', validator: type => ['date', 'month'/*, 'year'*/].includes(type) }, value: String, // Function formatting the year in table header and pickup title yearFormat: { type: Function, default: null }, yearIcon: String }, computed: { current () { if (this.showCurrent === true) { return this.sanitizeDateString(`${this.now.getFullYear()}-${this.now.getMonth() + 1}-${this.now.getDate()}`, this.type) } return this.showCurrent || null }, firstAllowedDate () { const year = this.now.getFullYear() const month = this.now.getMonth() if (this.allowedDates) { for (let date = 1; date <= 31; date++) { const dateString = `${year}-${month + 1}-${date}` if (isNaN(new Date(dateString).getDate())) break const sanitizedDateString = this.sanitizeDateString(dateString, 'date') if (isValueAllowed(sanitizedDateString, this.allowedDates)) { return sanitizedDateString } } } return this.sanitizeDateString(`${year}-${month + 1}-${this.now.getDate()}`, 'date') }, firstAllowedMonth () { const year = this.now.getFullYear() if (this.allowedDates) { for (let month = 0; month < 12; month++) { const dateString = `${year}-${pad(month + 1)}` if (isValueAllowed(dateString, this.allowedDates)) { return dateString } } } return `${year}-${pad(this.now.getMonth() + 1)}` }, // inputDate MUST be a string in ISO 8601 format (including leading zero for month/day) // YYYY-MM for month picker // YYYY-MM-DD for date picker inputDate: { get () { if (this.value) { return this.sanitizeDateString(this.value, this.type) } return this.type === 'month' ? this.firstAllowedMonth : this.firstAllowedDate }, set (value) { const date = value == null ? this.originalDate : this.sanitizeDateString(value, this.type) this.$emit('input', date) } }, day () { return this.inputDate.split('-')[2] * 1 }, month () { return this.inputDate.split('-')[1] - 1 }, year () { return this.inputDate.split('-')[0] * 1 }, tableMonth () { return this.tableDate.split('-')[1] - 1 }, tableYear () { return this.tableDate.split('-')[0] * 1 }, formatters () { return { year: this.yearFormat || createNativeLocaleFormatter(this.locale, { year: 'numeric', timeZone: 'UTC' }, { length: 4 }), titleDate: this.titleDateFormat || this.defaultTitleDateFormatter } }, defaultTitleDateFormatter () { const titleFormats = { year: { year: 'numeric', timeZone: 'UTC' }, month: { month: 'long', timeZone: 'UTC' }, date: { weekday: 'short', month: 'short', day: 'numeric', timeZone: 'UTC' } } const titleDateFormatter = createNativeLocaleFormatter(this.locale, titleFormats[this.type], { start: 0, length: { date: 10, month: 7, year: 4 }[this.type] }) const landscapeFormatter = date => titleDateFormatter(date) .replace(/([^\d\s])([\d])/g, (match, nonDigit, digit) => `${nonDigit} ${digit}`) .replace(', ', ',<br>') return this.landscape ? landscapeFormatter : titleDateFormatter } }, watch: { tableDate (val, prev) { // Make a ISO 8601 strings from val and prev for comparision, otherwise it will incorrectly // compare for example '2000-9' and '2000-10' const sanitizeType = this.type === 'month' ? 'year' : 'month' this.isReversing = this.sanitizeDateString(val, sanitizeType) < this.sanitizeDateString(prev, sanitizeType) }, value (val) { val && this.setTableDate() }, type (type) { this.activePicker = type.toUpperCase() if (this.value) { const date = this.sanitizeDateString(this.value, type) this.$emit('input', isValueAllowed(date, this.allowedDates) ? date : null) } } }, created () { this.setTableDate() }, methods: { setTableDate () { this.tableDate = this.type === 'month' ? `${this.year}` : `${this.year}-${pad(this.month + 1)}` }, save () { if (this.originalDate) { this.originalDate = this.value } else { this.originalDate = this.inputDate } if (this.$parent && this.$parent.isActive) this.$parent.isActive = false }, cancel () { this.inputDate = this.originalDate if (this.$parent && this.$parent.isActive) this.$parent.isActive = false }, yearClick (value) { if (this.type === 'month') { const date = `${value}-${pad(this.month + 1)}` if (isValueAllowed(date, this.allowedDates)) this.inputDate = date this.tableDate = `${value}` } else { const date = `${value}-${pad(this.tableMonth + 1)}-${pad(this.day)}` if (isValueAllowed(date, this.allowedDates)) this.inputDate = date this.tableDate = `${value}-${pad(this.tableMonth + 1)}` } this.activePicker = 'MONTH' }, monthClick (value) { // Updates inputDate setting 'YYYY-MM' or 'YYYY-MM-DD' format, depending on the picker type if (this.type === 'date') { const date = `${value}-${pad(this.day)}` if (isValueAllowed(date, this.allowedDates)) this.inputDate = date this.tableDate = value this.activePicker = 'DATE' } else { this.inputDate = value this.$nextTick(() => (this.autosave && this.save())) } }, dateClick (value) { this.inputDate = value this.$nextTick(() => (this.autosave && this.save())) }, genPickerTitle () { return this.$createElement('v-date-picker-title', { props: { date: this.formatters.titleDate(this.inputDate), selectingYear: this.activePicker === 'YEAR', year: this.formatters.year(`${this.year}`), yearIcon: this.yearIcon }, slot: 'title', style: this.readonly ? { 'pointer-events': 'none' } : undefined, on: { 'update:selectingYear': value => this.activePicker = value ? 'YEAR' : this.type.toUpperCase() } }) }, genTableHeader () { return this.$createElement('v-date-picker-header', { props: { appendIcon: this.appendIcon, color: this.color, disabled: this.readonly, format: this.headerDateFormat, locale: this.locale, prependIcon: this.prependIcon, value: this.activePicker === 'DATE' ? this.tableDate : `${this.tableYear}` }, on: { toggle: () => this.activePicker = (this.activePicker === 'DATE' ? 'MONTH' : 'YEAR'), input: value => this.tableDate = value } }) }, genDateTable () { return this.$createElement('v-date-picker-date-table', { props: { allowedDates: this.allowedDates, color: this.color, current: this.current, disabled: this.readonly, events: this.events, eventColor: this.eventColor, firstDayOfWeek: this.firstDayOfWeek, format: this.dayFormat, locale: this.locale, tableDate: this.tableDate, scrollable: this.scrollable, value: this.value }, ref: 'table', on: { input: this.dateClick, tableDate: value => this.tableDate = value } }) }, genMonthTable () { return this.$createElement('v-date-picker-month-table', { props: { allowedDates: this.type === 'month' ? this.allowedDates : null, color: this.color, current: this.current, disabled: this.readonly, format: this.monthFormat, locale: this.locale, scrollable: this.scrollable, value: (!this.value || this.type === 'month') ? this.value : this.value.substr(0, 7), tableDate: `${this.tableYear}` }, ref: 'table', on: { input: this.monthClick, tableDate: value => this.tableDate = value } }) }, genYears () { return this.$createElement('v-date-picker-years', { props: { color: this.color, format: this.yearFormat, locale: this.locale, value: `${this.tableYear}` }, on: { input: this.yearClick } }) }, genPickerBody () { const children = this.activePicker === 'YEAR' ? [ this.genYears() ] : [ this.genTableHeader(), this.activePicker === 'DATE' ? this.genDateTable() : this.genMonthTable() ] return this.$createElement('div', { key: this.activePicker, style: this.readonly ? { 'pointer-events': 'none' } : undefined }, children) }, // Adds leading zero to month/day if necessary, returns 'YYYY' if type = 'year', // 'YYYY-MM' if 'month' and 'YYYY-MM-DD' if 'date' sanitizeDateString (dateString, type) { const [year, month = 1, date = 1] = dateString.split('-') return `${year}-${pad(month)}-${pad(date)}`.substr(0, { date: 10, month: 7, year: 4 }[type]) } }, render (h) { return this.genPicker('picker--date') } }