mirror of
https://github.com/tenrok/vue2-datepicker.git
synced 2026-06-22 04:10:36 +03:00
380 lines
12 KiB
Vue
380 lines
12 KiB
Vue
<template>
|
|
<div
|
|
:class="[
|
|
`${prefixClass}-calendar`,
|
|
`${prefixClass}-calendar-panel-${panel}`,
|
|
{ [`${prefixClass}-calendar-week-mode`]: type === 'week' },
|
|
]"
|
|
>
|
|
<div :class="`${prefixClass}-calendar-header`">
|
|
<button
|
|
v-show="showIconDoubleArrow"
|
|
type="button"
|
|
:class="`${prefixClass}-btn ${prefixClass}-btn-text ${prefixClass}-btn-icon-double-left`"
|
|
@click="handleIconDoubleLeftClick"
|
|
>
|
|
<i :class="`${prefixClass}-icon-double-left`"></i>
|
|
</button>
|
|
<button
|
|
v-show="showIconArrow"
|
|
type="button"
|
|
:class="`${prefixClass}-btn ${prefixClass}-btn-text ${prefixClass}-btn-icon-left`"
|
|
@click="handleIconLeftClick"
|
|
>
|
|
<i :class="`${prefixClass}-icon-left`"></i>
|
|
</button>
|
|
<button
|
|
v-show="showIconDoubleArrow"
|
|
type="button"
|
|
:class="`${prefixClass}-btn ${prefixClass}-btn-text ${prefixClass}-btn-icon-double-right`"
|
|
@click="handleIconDoubleRightClick"
|
|
>
|
|
<i :class="`${prefixClass}-icon-double-right`"></i>
|
|
</button>
|
|
<button
|
|
v-show="showIconArrow"
|
|
type="button"
|
|
:class="`${prefixClass}-btn ${prefixClass}-btn-text ${prefixClass}-btn-icon-right`"
|
|
@click="handleIconRightClick"
|
|
>
|
|
<i :class="`${prefixClass}-icon-right`"></i>
|
|
</button>
|
|
<span :class="`${prefixClass}-calendar-header-label`">
|
|
<template v-if="panel === 'year'">
|
|
<span>{{ calendarDecade }}</span>
|
|
<span :class="`${prefixClass}-calendar-decade-separator`"></span>
|
|
<span>{{ calendarDecade + 9 }}</span>
|
|
</template>
|
|
<button
|
|
v-else-if="panel === 'month'"
|
|
type="button"
|
|
:class="`${prefixClass}-btn ${prefixClass}-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="
|
|
`${prefixClass}-btn ${prefixClass}-btn-text ${prefixClass}-btn-current-${item.panel}`
|
|
"
|
|
@click="handelPanelChange(item.panel)"
|
|
>
|
|
{{ item.label }}
|
|
</button>
|
|
</template>
|
|
</span>
|
|
</div>
|
|
<div :class="`${prefixClass}-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';
|
|
import emitter from '../mixin/emitter';
|
|
|
|
export default {
|
|
name: 'CalendarPanel',
|
|
components: {
|
|
TableDate,
|
|
TableMonth,
|
|
TableYear,
|
|
},
|
|
mixins: [emitter],
|
|
inject: {
|
|
translateFn: {
|
|
default: () => getLocaleFieldValue,
|
|
},
|
|
prefixClass: {
|
|
default: 'mx',
|
|
},
|
|
},
|
|
props: {
|
|
value: {},
|
|
defaultValue: {
|
|
default() {
|
|
const date = new Date();
|
|
date.setHours(0, 0, 0, 0);
|
|
return date;
|
|
},
|
|
},
|
|
defaultPanel: {
|
|
type: String,
|
|
},
|
|
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', 'month', 'year'];
|
|
const index = Math.max(panels.indexOf(this.type), panels.indexOf(this.defaultPanel));
|
|
const panel = index !== -1 ? panels[index] : '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.translateFn('monthBeforeYear');
|
|
const yearFormat = this.translateFn('yearFormat');
|
|
const monthFormat = this.translateFn('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.translateFn('formatLocale') });
|
|
},
|
|
initCalendar() {
|
|
let calendarDate = this.calendar;
|
|
if (!isValidDate(calendarDate)) {
|
|
const { length } = this.innerValue;
|
|
calendarDate = getValidDate(length > 0 ? this.innerValue[length - 1] : 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, this.innerValue);
|
|
// someone need get the first selected date to set range value. (#429)
|
|
this.dispatch('DatePicker', 'pick', date, type);
|
|
}
|
|
},
|
|
updateCalendar(date, type) {
|
|
const oldValue = new Date(this.innerCalendar);
|
|
this.innerCalendar = date;
|
|
this.$emit('update:calendar', date);
|
|
this.dispatch('DatePicker', 'calendar-change', date, oldValue, type);
|
|
},
|
|
handelPanelChange(panel) {
|
|
this.panel = panel;
|
|
},
|
|
handleIconLeftClick() {
|
|
const nextCalendar = subMonths(this.innerCalendar, 1);
|
|
this.updateCalendar(nextCalendar, 'last-month');
|
|
},
|
|
handleIconRightClick() {
|
|
const nextCalendar = addMonths(this.innerCalendar, 1);
|
|
this.updateCalendar(nextCalendar, 'next-month');
|
|
},
|
|
handleIconDoubleLeftClick() {
|
|
const nextCalendar = subYears(this.innerCalendar, this.panel === 'year' ? 10 : 1);
|
|
this.updateCalendar(nextCalendar, this.panel === 'year' ? 'last-decade' : 'last-year');
|
|
},
|
|
handleIconDoubleRightClick() {
|
|
const nextCalendar = addYears(this.innerCalendar, this.panel === 'year' ? 10 : 1);
|
|
this.updateCalendar(nextCalendar, this.panel === 'year' ? 'next-decade' : 'next-year');
|
|
},
|
|
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, 'year');
|
|
this.handelPanelChange('month');
|
|
if (this.partialUpdate && this.innerValue.length === 1) {
|
|
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, 'month');
|
|
this.handelPanelChange('date');
|
|
if (this.partialUpdate && this.innerValue.length === 1) {
|
|
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 ? `${this.prefixClass}-active-week` : '';
|
|
},
|
|
},
|
|
};
|
|
</script>
|