2
0
mirror of https://github.com/tenrok/vue2-datepicker.git synced 2026-06-21 05:20:35 +03:00
Files
vue2-datepicker/src/calendar/calendar-panel.vue
T
2019-12-05 11:01:57 +08:00

367 lines
10 KiB
Vue

<template>
<div
:class="[
'mx-calendar',
`mx-calendar-panel-${panel}`,
{ 'mx-calendar-week-mode': type === 'week' },
]"
>
<div class="mx-calendar-header">
<button
v-show="showIconDoubleArrow"
type="button"
class="mx-btn mx-btn-text mx-btn-icon-double-left"
@click="handleIconDoubleLeftClick"
>
<i class="mx-icon-double-left"></i>
</button>
<button
v-show="showIconArrow"
type="button"
class="mx-btn mx-btn-text mx-btn-icon-left"
@click="handleIconLeftClick"
>
<i class="mx-icon-left"></i>
</button>
<button
v-show="showIconDoubleArrow"
type="button"
class="mx-btn mx-btn-text mx-btn-icon-double-right"
@click="handleIconDoubleRightClick"
>
<i class="mx-icon-double-right"></i>
</button>
<button
v-show="showIconArrow"
type="button"
class="mx-btn mx-btn-text mx-btn-icon-right"
@click="handleIconRightClick"
>
<i class="mx-icon-right"></i>
</button>
<span class="mx-calendar-header-label">
<template v-if="panel === 'year'">
<span>{{ calendarDecade }}</span>
<span class="mx-calendar-decade-separator"></span>
<span>{{ calendarDecade + 9 }}</span>
</template>
<button
v-else-if="panel === 'month'"
type="button"
class="mx-btn mx-btn-text"
@click="handelPanelChange('year')"
>
{{ calendarYear }}
</button>
<template v-else-if="panel === 'date'">
<button
v-for="item in dateHeader"
:key="item.panel"
type="button"
:class="`mx-btn mx-btn-text mx-btn-current-${item.panel}`"
@click="handelPanelChange(item.panel)"
>
{{ item.label }}
</button>
</template>
</span>
</div>
<div class="mx-calendar-content">
<table-year
v-show="panel === 'year'"
:decade="calendarDecade"
:get-cell-classes="getYearClasses"
@select="handleSelectYear"
></table-year>
<table-month
v-if="type !== 'year'"
v-show="panel === 'month'"
:get-cell-classes="getMonthClasses"
@select="handleSelectMonth"
></table-month>
<table-date
v-if="type !== 'year' && type !== 'month'"
v-show="panel === 'date'"
:calendar-year="calendarYear"
:calendar-month="calendarMonth"
:title-format="titleFormat"
:show-week-number="typeof showWeekNumber === 'boolean' ? showWeekNumber : type === 'week'"
:get-cell-classes="getDateClasses"
:get-row-classes="getWeekState"
@select="handleSelectDate"
></table-date>
</div>
</div>
</template>
<script>
import {
subMonths,
addMonths,
subYears,
addYears,
setMonth,
setYear,
startOfYear,
startOfMonth,
startOfDay,
} from 'date-fns';
import { format } from 'date-format-parse';
import { getValidDate, isValidDate, createDate } from '../util/date';
import TableDate from './table-date';
import TableMonth from './table-month';
import TableYear from './table-year';
import { getLocaleFieldValue } from '../locale';
export default {
name: 'CalendarPanel',
components: {
TableDate,
TableMonth,
TableYear,
},
inject: {
t: {
default: () => getLocaleFieldValue,
},
},
props: {
value: {},
defaultValue: {
validator(value) {
return !isNaN(new Date(value).getTime());
},
default() {
const date = new Date();
date.setHours(0, 0, 0, 0);
return date;
},
},
disabledDate: {
type: Function,
default: () => false,
},
type: {
type: String,
default: 'date',
},
getClasses: {
type: Function,
default: () => [],
},
showWeekNumber: {
type: Boolean,
default: undefined,
},
titleFormat: {
type: String,
default: 'YYYY-MM-DD',
},
calendar: Date,
// update date when select year or month
partialUpdate: {
type: Boolean,
default: false,
},
},
data() {
const panels = ['date', 'year', 'month'];
const panel = panels.indexOf(this.type) !== -1 ? this.type : 'date';
return {
panel,
innerCalendar: null,
};
},
computed: {
innerValue() {
const value = Array.isArray(this.value) ? this.value : [this.value];
const map = {
year: startOfYear,
month: startOfMonth,
date: startOfDay,
};
const start = map[this.type] || map.date;
return value.filter(isValidDate).map(v => start(v));
},
calendarYear() {
return this.innerCalendar.getFullYear();
},
calendarMonth() {
return this.innerCalendar.getMonth();
},
calendarDecade() {
return Math.floor(this.calendarYear / 10) * 10;
},
showIconDoubleArrow() {
return this.panel === 'date' || this.panel === 'month' || this.panel === 'year';
},
showIconArrow() {
return this.panel === 'date';
},
dateHeader() {
const monthBeforeYear = this.t('monthBeforeYear');
const yearFormat = this.t('yearFormat');
const monthFormat = this.t('monthFormat') || 'MMM';
const yearLabel = {
panel: 'year',
label: this.formatDate(this.innerCalendar, yearFormat),
};
const monthLabel = {
panel: 'month',
label: this.formatDate(this.innerCalendar, monthFormat),
};
return monthBeforeYear ? [monthLabel, yearLabel] : [yearLabel, monthLabel];
},
},
watch: {
value: {
immediate: true,
handler: 'initCalendar',
},
calendar: {
handler: 'initCalendar',
},
defaultValue: {
handler: 'initCalendar',
},
},
methods: {
formatDate(date, fmt) {
return format(date, fmt, { locale: this.t('formatLocale') });
},
initCalendar() {
let calendarDate = this.calendar;
if (!isValidDate(calendarDate)) {
calendarDate = getValidDate(this.innerValue[0], this.defaultValue);
}
this.innerCalendar = calendarDate;
},
isDisabled(date) {
return this.disabledDate(new Date(date), this.innerValue);
},
emitDate(date, type) {
if (!this.isDisabled(date)) {
this.$emit('select', date, type);
}
},
updateCalendar(date) {
this.innerCalendar = date;
this.$emit('update:calendar', date);
},
handelPanelChange(panel) {
this.panel = panel;
},
handleIconLeftClick() {
const nextCalendar = subMonths(this.innerCalendar, 1);
this.updateCalendar(nextCalendar);
},
handleIconRightClick() {
const nextCalendar = addMonths(this.innerCalendar, 1);
this.updateCalendar(nextCalendar);
},
handleIconDoubleLeftClick() {
const nextCalendar = subYears(this.innerCalendar, this.panel === 'year' ? 10 : 1);
this.updateCalendar(nextCalendar);
},
handleIconDoubleRightClick() {
const nextCalendar = addYears(this.innerCalendar, this.panel === 'year' ? 10 : 1);
this.updateCalendar(nextCalendar);
},
handleSelectYear(year) {
if (this.type === 'year') {
const date = this.getCellDate(year, 'year');
this.emitDate(date, 'year');
} else {
const nextCalendar = setYear(this.innerCalendar, year);
this.updateCalendar(nextCalendar);
this.handelPanelChange('month');
if (this.partialUpdate && this.innerValue[0]) {
const date = setYear(this.innerValue[0], year);
this.emitDate(date, 'year');
}
}
},
handleSelectMonth(month) {
if (this.type === 'month') {
const date = this.getCellDate(month, 'month');
this.emitDate(date, 'month');
} else {
const nextCalendar = setMonth(this.innerCalendar, month);
this.updateCalendar(nextCalendar);
this.handelPanelChange('date');
if (this.partialUpdate && this.innerValue[0]) {
const date = setMonth(setYear(this.innerValue[0], this.calendarYear), month);
this.emitDate(date, 'month');
}
}
},
handleSelectDate(day) {
const date = this.getCellDate(day, 'date');
this.emitDate(date, this.type === 'week' ? 'week' : 'date');
},
getCellDate(value, type) {
if (type === 'year') {
return createDate(value, 0);
}
if (type === 'month') {
return createDate(this.calendarYear, value);
}
return createDate(this.calendarYear, this.calendarMonth, value);
},
getDateClasses(day) {
const cellDate = this.getCellDate(day, 'date');
const notCurrentMonth = cellDate.getMonth() !== this.calendarMonth;
const classes = [];
if (cellDate.getTime() === new Date().setHours(0, 0, 0, 0)) {
classes.push('today');
}
if (notCurrentMonth) {
classes.push('not-current-month');
}
const state = this.getStateClass(cellDate);
if (!(state === 'active' && notCurrentMonth)) {
classes.push(state);
}
return classes.concat(this.getClasses(cellDate, this.innerValue, classes.join(' ')));
},
getMonthClasses(month) {
if (this.type !== 'month') {
return this.calendarMonth === month ? 'active' : '';
}
const classes = [];
const cellDate = this.getCellDate(month, 'month');
classes.push(this.getStateClass(cellDate));
return classes.concat(this.getClasses(cellDate, this.innerValue, classes.join(' ')));
},
getYearClasses(year) {
if (this.type !== 'year') {
return this.calendarYear === year ? 'active' : '';
}
const classes = [];
const cellDate = this.getCellDate(year, 'year');
classes.push(this.getStateClass(cellDate));
return classes.concat(this.getClasses(cellDate, this.innerValue, classes.join(' ')));
},
getStateClass(cellDate) {
if (this.isDisabled(cellDate)) {
return 'disabled';
}
if (this.innerValue.some(v => v.getTime() === cellDate.getTime())) {
return 'active';
}
return '';
},
getWeekState(row) {
if (this.type !== 'week') return '';
const start = this.getCellDate(row[0].day, 'date').getTime();
const end = this.getCellDate(row[6].day, 'date').getTime();
const active = this.innerValue.some(v => {
const time = v.getTime();
return time >= start && time <= end;
});
return active ? 'mx-active-week' : '';
},
},
};
</script>