|
|
@@ -1,894 +0,0 @@
|
|
|
-<template>
|
|
|
- <div class="vdpComponent" :class="{vdpWithInput: hasInputElement}">
|
|
|
- <slot
|
|
|
- :open="open"
|
|
|
- :close="close"
|
|
|
- :toggle="toggle"
|
|
|
- :inputValue="inputValue"
|
|
|
- :processUserInput="processUserInput"
|
|
|
- :valueToInputFormat="valueToInputFormat"
|
|
|
- >
|
|
|
- <input
|
|
|
- v-if="hasInputElement"
|
|
|
- type="text"
|
|
|
- v-bind="inputAttributes"
|
|
|
- :readonly="isReadOnly"
|
|
|
- :value="inputValue"
|
|
|
- @input="editable && processUserInput($event.target.value)"
|
|
|
- @focus="editable && open()"
|
|
|
- @click="editable && open()"
|
|
|
- :placeholder="placeholder"
|
|
|
- >
|
|
|
- <h-icon type="guanbi" v-if="editable && hasInputElement && inputValue && showClear"
|
|
|
- class="vdpClearInput"
|
|
|
- @click="clear" />
|
|
|
- </slot>
|
|
|
- <transition name="vdp-toggle-calendar">
|
|
|
- <div
|
|
|
- v-if="opened"
|
|
|
- class="vdpOuterWrap"
|
|
|
- ref="outerWrap"
|
|
|
- @click="closeViaOverlay"
|
|
|
- :class="[positionClass, {vdpFloating: hasInputElement}]"
|
|
|
- >
|
|
|
- <div class="vdpInnerWrap">
|
|
|
- <header class="vdpHeader">
|
|
|
- <button
|
|
|
- class="vdpArrow vdpArrowPrev"
|
|
|
- :title="prevMonthCaption"
|
|
|
- type="button"
|
|
|
- @click="incrementMonth(-1)"
|
|
|
- >{{ prevMonthCaption }}</button>
|
|
|
- <button
|
|
|
- class="vdpArrow vdpArrowNext"
|
|
|
- type="button"
|
|
|
- :title="nextMonthCaption"
|
|
|
- @click="incrementMonth(1)"
|
|
|
- >{{ nextMonthCaption }}</button>
|
|
|
- <div class="vdpPeriodControls">
|
|
|
- <div class="vdpPeriodControl">
|
|
|
- <button :class="directionClass" :key="currentPeriod.month" type="button">
|
|
|
- {{ months[currentPeriod.month] }}
|
|
|
- </button>
|
|
|
- <select v-model="currentPeriod.month">
|
|
|
- <option v-for="(month, index) in months" :value="index" :key="month">
|
|
|
- {{ month }}
|
|
|
- </option>
|
|
|
- </select>
|
|
|
- </div>
|
|
|
- <div class="vdpPeriodControl">
|
|
|
- <button :class="directionClass" :key="currentPeriod.year" type="button">
|
|
|
- {{ currentPeriod.year }}
|
|
|
- </button>
|
|
|
- <select v-model="currentPeriod.year">
|
|
|
- <option v-for="year in yearRange" :value="year" :key="year">
|
|
|
- {{ year }}
|
|
|
- </option>
|
|
|
- </select>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </header>
|
|
|
- <table class="vdpTable">
|
|
|
- <thead>
|
|
|
- <tr>
|
|
|
- <th class="vdpHeadCell" v-for="(weekday, weekdayIndex) in weekdaysSorted" :key="weekdayIndex">
|
|
|
- <span class="vdpHeadCellContent">{{weekday}}</span>
|
|
|
- </th>
|
|
|
- </tr>
|
|
|
- </thead>
|
|
|
- <tbody
|
|
|
- :key="currentPeriod.year + '-' + currentPeriod.month"
|
|
|
- :class="directionClass"
|
|
|
- >
|
|
|
- <tr class="vdpRow" v-for="(week, weekIndex) in currentPeriodDates" :key="weekIndex">
|
|
|
- <td
|
|
|
- class="vdpCell"
|
|
|
- v-for="item in week"
|
|
|
- :class="{
|
|
|
- selectable: editable && !item.disabled,
|
|
|
- selected: item.selected,
|
|
|
- disabled: item.disabled,
|
|
|
- today: item.today,
|
|
|
- outOfRange: item.outOfRange
|
|
|
- }"
|
|
|
- :data-id="item.dateKey"
|
|
|
- :key="item.dateKey"
|
|
|
- @click="editable && selectDateItem(item)"
|
|
|
- >
|
|
|
- <div
|
|
|
- class="vdpCellContent"
|
|
|
- >{{ item.date.getDate() }}</div>
|
|
|
- </td>
|
|
|
- </tr>
|
|
|
- </tbody>
|
|
|
- </table>
|
|
|
- <div v-if="pickTime && currentTime" class="vdpTimeControls">
|
|
|
- <span class="vdpTimeCaption">{{ setTimeCaption }}</span>
|
|
|
- <div class="vdpTimeUnit">
|
|
|
- <pre><span>{{ currentTime.hoursFormatted }}</span><br></pre>
|
|
|
- <input
|
|
|
- type="number" pattern="\d*" class="vdpHoursInput"
|
|
|
- @input.prevent="inputHours"
|
|
|
- @focusin="onTimeInputFocus"
|
|
|
- :disabled="!editable"
|
|
|
- :value="currentTime.hoursFormatted"
|
|
|
- >
|
|
|
- </div>
|
|
|
- <span v-if="pickMinutes" class="vdpTimeSeparator">:</span>
|
|
|
- <div v-if="pickMinutes" class="vdpTimeUnit">
|
|
|
- <pre><span>{{ currentTime.minutesFormatted }}</span><br></pre>
|
|
|
- <input
|
|
|
- v-if="pickMinutes"
|
|
|
- type="number" pattern="\d*" class="vdpMinutesInput"
|
|
|
- @input="inputTime('setMinutes', $event)"
|
|
|
- @focusin="onTimeInputFocus"
|
|
|
- :disabled="!editable"
|
|
|
- :value="currentTime.minutesFormatted"
|
|
|
- >
|
|
|
- </div>
|
|
|
- <span v-if="pickSeconds" class="vdpTimeSeparator">:</span>
|
|
|
- <div v-if="pickSeconds" class="vdpTimeUnit">
|
|
|
- <pre><span>{{ currentTime.secondsFormatted }}</span><br></pre>
|
|
|
- <input
|
|
|
- v-if="pickSeconds"
|
|
|
- type="number" pattern="\d*" class="vdpSecondsInput"
|
|
|
- @input="inputTime('setSeconds', $event)"
|
|
|
- @focusin="onTimeInputFocus"
|
|
|
- :disabled="!editable"
|
|
|
- :value="currentTime.secondsFormatted"
|
|
|
- >
|
|
|
- </div>
|
|
|
- <button
|
|
|
- v-if="use12HourClock"
|
|
|
- type="button" class="vdp12HourToggleBtn"
|
|
|
- :disabled="!editable"
|
|
|
- @click="set12HourClock(currentTime.isPM ? 'AM' : 'PM')"
|
|
|
- >
|
|
|
- {{ currentTime.isPM ? 'PM' : 'AM' }}
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </transition>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script>
|
|
|
-
|
|
|
-const formatRE = /,|\.|-| |:|\/|\\/;
|
|
|
-const dayRE = /D+/;
|
|
|
-const monthRE = /M+/;
|
|
|
-const yearRE = /Y+/;
|
|
|
-const hoursRE = /h+/i;
|
|
|
-const minutesRE = /m+/;
|
|
|
-const secondsRE = /s+/;
|
|
|
-const AMPMClockRE = /A/;
|
|
|
-
|
|
|
-export default {
|
|
|
-
|
|
|
- props: {
|
|
|
- value: {
|
|
|
- type: String,
|
|
|
- default: ''
|
|
|
- },
|
|
|
- format: {
|
|
|
- type: String,
|
|
|
- default: 'YYYY-MM-DD'
|
|
|
- },
|
|
|
- displayFormat: {
|
|
|
- type: String
|
|
|
- },
|
|
|
- editable: {
|
|
|
- type: Boolean,
|
|
|
- default: true
|
|
|
- },
|
|
|
- hasInputElement: {
|
|
|
- type: Boolean,
|
|
|
- default: true
|
|
|
- },
|
|
|
- inputAttributes: {
|
|
|
- type: Object
|
|
|
- },
|
|
|
- selectableYearRange: {
|
|
|
- type: [Number, Object, Function],
|
|
|
- default: 40
|
|
|
- },
|
|
|
- startPeriod: {
|
|
|
- type: Object
|
|
|
- },
|
|
|
- parseDate: {
|
|
|
- type: Function
|
|
|
- },
|
|
|
- formatDate: {
|
|
|
- type: Function
|
|
|
- },
|
|
|
- pickTime: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- pickMinutes: {
|
|
|
- type: Boolean,
|
|
|
- default: true
|
|
|
- },
|
|
|
- pickSeconds: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- use12HourClock: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- isDateDisabled: {
|
|
|
- type: Function,
|
|
|
- default: () => false
|
|
|
- },
|
|
|
- nextMonthCaption: {
|
|
|
- type: String,
|
|
|
- default: 'Next month'
|
|
|
- },
|
|
|
- prevMonthCaption: {
|
|
|
- type: String,
|
|
|
- default: 'Previous month'
|
|
|
- },
|
|
|
- setTimeCaption: {
|
|
|
- type: String,
|
|
|
- default: 'Set time:'
|
|
|
- },
|
|
|
- mobileBreakpointWidth: {
|
|
|
- type: Number,
|
|
|
- default: 500
|
|
|
- },
|
|
|
- weekdays: {
|
|
|
- type: Array,
|
|
|
- default: () => ([
|
|
|
- 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'
|
|
|
- ])
|
|
|
- },
|
|
|
- months: {
|
|
|
- type: Array,
|
|
|
- default: () => ([
|
|
|
- 'January', 'February', 'March', 'April',
|
|
|
- 'May', 'June', 'July', 'August',
|
|
|
- 'September', 'October', 'November', 'December'
|
|
|
- ])
|
|
|
- },
|
|
|
- startWeekOnSunday: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- placeholder: String,
|
|
|
- showClear: Boolean
|
|
|
- },
|
|
|
-
|
|
|
- data() {
|
|
|
- return {
|
|
|
- inputValue: this.valueToInputFormat(this.value),
|
|
|
- direction: undefined,
|
|
|
- positionClass: undefined,
|
|
|
- opened: !this.hasInputElement,
|
|
|
- currentPeriod: this.startPeriod || this.getPeriodFromValue(
|
|
|
- this.value, this.format
|
|
|
- )
|
|
|
- };
|
|
|
- },
|
|
|
-
|
|
|
- computed: {
|
|
|
-
|
|
|
- valueDate() {
|
|
|
-
|
|
|
- const value = this.value;
|
|
|
- const format = this.format;
|
|
|
-
|
|
|
- return value
|
|
|
- ? this.parseDateString(value, format)
|
|
|
- : undefined
|
|
|
- ;
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- isReadOnly() {
|
|
|
- return !this.editable || (this.inputAttributes && this.inputAttributes.readonly);
|
|
|
- },
|
|
|
-
|
|
|
- isValidValue() {
|
|
|
-
|
|
|
- const valueDate = this.valueDate;
|
|
|
-
|
|
|
- return this.value ? Boolean(valueDate) : true;
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- currentPeriodDates() {
|
|
|
-
|
|
|
- const {year, month} = this.currentPeriod;
|
|
|
- const days = [];
|
|
|
- const date = new Date(year, month, 1);
|
|
|
- const today = new Date();
|
|
|
- const offset = this.startWeekOnSunday ? 1 : 0;
|
|
|
-
|
|
|
- // append prev month dates
|
|
|
- const startDay = date.getDay() || 7;
|
|
|
-
|
|
|
- if (startDay > (1 - offset)) {
|
|
|
- for (let i = startDay - (2 - offset); i >= 0; i--) {
|
|
|
-
|
|
|
- const prevDate = new Date(date);
|
|
|
- prevDate.setDate(-i);
|
|
|
- days.push({outOfRange: true, date: prevDate});
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- while (date.getMonth() === month) {
|
|
|
- days.push({date: new Date(date)});
|
|
|
- date.setDate(date.getDate() + 1);
|
|
|
- }
|
|
|
-
|
|
|
- // append next month dates
|
|
|
- const daysLeft = 7 - days.length % 7;
|
|
|
-
|
|
|
- for (let i = 1; i <= daysLeft; i++) {
|
|
|
-
|
|
|
- const nextDate = new Date(date);
|
|
|
- nextDate.setDate(i);
|
|
|
- days.push({outOfRange: true, date: nextDate});
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // define day states
|
|
|
- days.forEach(day => {
|
|
|
- day.disabled = this.isDateDisabled(day.date);
|
|
|
- day.today = areSameDates(day.date, today);
|
|
|
- day.dateKey = [
|
|
|
- day.date.getFullYear(), day.date.getMonth() + 1, day.date.getDate()
|
|
|
- ].join('-');
|
|
|
- day.selected = this.valueDate ? areSameDates(day.date, this.valueDate) : false;
|
|
|
- });
|
|
|
-
|
|
|
- return chunkArray(days, 7);
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- yearRange() {
|
|
|
-
|
|
|
- const currentYear = this.currentPeriod.year;
|
|
|
- const userRange = this.selectableYearRange;
|
|
|
- const userRangeType = typeof userRange;
|
|
|
-
|
|
|
- let yearsRange = [];
|
|
|
-
|
|
|
- if (userRangeType === 'number') {
|
|
|
- yearsRange = range(
|
|
|
- currentYear - userRange,
|
|
|
- currentYear + userRange
|
|
|
- );
|
|
|
- } else if (userRangeType === 'object') {
|
|
|
- yearsRange = range(
|
|
|
- userRange.from,
|
|
|
- userRange.to
|
|
|
- );
|
|
|
- } else if (userRangeType === 'function') {
|
|
|
- yearsRange = userRange(this);
|
|
|
- }
|
|
|
-
|
|
|
- if (yearsRange.indexOf(currentYear) < 0) {
|
|
|
- yearsRange.push(currentYear);
|
|
|
- yearsRange = yearsRange.sort();
|
|
|
- }
|
|
|
-
|
|
|
- return yearsRange;
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- currentTime() {
|
|
|
-
|
|
|
- const currentDate = this.valueDate;
|
|
|
-
|
|
|
- if (!currentDate) {
|
|
|
- return undefined;
|
|
|
- }
|
|
|
-
|
|
|
- const hours = currentDate.getHours();
|
|
|
- const minutes = currentDate.getMinutes();
|
|
|
- const seconds = currentDate.getSeconds();
|
|
|
-
|
|
|
- return {
|
|
|
- hours,
|
|
|
- minutes,
|
|
|
- seconds,
|
|
|
- isPM: isPM(hours),
|
|
|
- hoursFormatted: (this.use12HourClock ? to12HourClock(hours) : hours).toString(),
|
|
|
- minutesFormatted: paddNum(minutes, 2),
|
|
|
- secondsFormatted: paddNum(seconds, 2)
|
|
|
- };
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- directionClass() {
|
|
|
-
|
|
|
- return this.direction ? `vdp${this.direction}Direction` : undefined;
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- weekdaysSorted() {
|
|
|
-
|
|
|
- if (this.startWeekOnSunday) {
|
|
|
- const weekdays = this.weekdays.slice();
|
|
|
- weekdays.unshift(weekdays.pop());
|
|
|
- return weekdays;
|
|
|
- } else {
|
|
|
- return this.weekdays;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- watch: {
|
|
|
-
|
|
|
- value(value) {
|
|
|
-
|
|
|
- if (this.isValidValue) {
|
|
|
- this.inputValue = this.valueToInputFormat(value);
|
|
|
- this.currentPeriod = this.getPeriodFromValue(value, this.format);
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- currentPeriod(currentPeriod, oldPeriod) {
|
|
|
-
|
|
|
- const currentDate = new Date(currentPeriod.year, currentPeriod.month).getTime();
|
|
|
- const oldDate = new Date(oldPeriod.year, oldPeriod.month).getTime();
|
|
|
-
|
|
|
- this.direction = currentDate !== oldDate
|
|
|
- ? (currentDate > oldDate ? 'Next' : 'Prev')
|
|
|
- : undefined
|
|
|
- ;
|
|
|
-
|
|
|
- if (currentDate !== oldDate) {
|
|
|
- this.$emit('periodChange', {
|
|
|
- year: currentPeriod.year,
|
|
|
- month: currentPeriod.month
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- beforeDestroy() {
|
|
|
-
|
|
|
- this.removeCloseEvents();
|
|
|
- this.teardownPosition();
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- methods: {
|
|
|
-
|
|
|
- valueToInputFormat(value) {
|
|
|
-
|
|
|
- return !this.displayFormat ? value : this.formatDateToString(
|
|
|
- this.parseDateString(value, this.format), this.displayFormat
|
|
|
- ) || value;
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- getPeriodFromValue(dateString, format) {
|
|
|
-
|
|
|
- const date = this.parseDateString(dateString, format) || new Date();
|
|
|
-
|
|
|
- return {month: date.getMonth(), year: date.getFullYear()};
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- parseDateString(dateString, dateFormat) {
|
|
|
-
|
|
|
- return !dateString
|
|
|
- ? undefined
|
|
|
- : this.parseDate
|
|
|
- ? this.parseDate(dateString, dateFormat)
|
|
|
- : this.parseSimpleDateString(dateString, dateFormat)
|
|
|
- ;
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- formatDateToString(date, dateFormat) {
|
|
|
-
|
|
|
- return !date
|
|
|
- ? ''
|
|
|
- : this.formatDate
|
|
|
- ? this.formatDate(date, dateFormat)
|
|
|
- : this.formatSimpleDateToString(date, dateFormat)
|
|
|
- ;
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- parseSimpleDateString(dateString, dateFormat) {
|
|
|
-
|
|
|
- let day, month, year, hours, minutes, seconds;
|
|
|
-
|
|
|
- const dateParts = dateString.split(formatRE);
|
|
|
- const formatParts = dateFormat.split(formatRE);
|
|
|
- const partsSize = formatParts.length;
|
|
|
-
|
|
|
- for (let i = 0; i < partsSize; i++) {
|
|
|
-
|
|
|
- if (formatParts[i].match(dayRE)) {
|
|
|
- day = parseInt(dateParts[i], 10);
|
|
|
- } else if (formatParts[i].match(monthRE)) {
|
|
|
- month = parseInt(dateParts[i], 10);
|
|
|
- } else if (formatParts[i].match(yearRE)) {
|
|
|
- year = parseInt(dateParts[i], 10);
|
|
|
- } else if (formatParts[i].match(hoursRE)) {
|
|
|
- hours = parseInt(dateParts[i], 10);
|
|
|
- } else if (formatParts[i].match(minutesRE)) {
|
|
|
- minutes = parseInt(dateParts[i], 10);
|
|
|
- } else if (formatParts[i].match(secondsRE)) {
|
|
|
- seconds = parseInt(dateParts[i], 10);
|
|
|
- }
|
|
|
-
|
|
|
- };
|
|
|
-
|
|
|
- const resolvedDate = new Date(
|
|
|
- [paddNum(year, 4), paddNum(month, 2), paddNum(day, 2)].join('-')
|
|
|
- );
|
|
|
-
|
|
|
- if (isNaN(resolvedDate)) {
|
|
|
- return undefined;
|
|
|
- } else {
|
|
|
-
|
|
|
- const date = new Date(year, month - 1, day);
|
|
|
-
|
|
|
- [
|
|
|
- [year, 'setFullYear'],
|
|
|
- [hours, 'setHours'],
|
|
|
- [minutes, 'setMinutes'],
|
|
|
- [seconds, 'setSeconds']
|
|
|
- ].forEach(([value, method]) => {
|
|
|
- typeof value !== 'undefined' && date[method](value);
|
|
|
- });
|
|
|
-
|
|
|
- return date;
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- formatSimpleDateToString(date, dateFormat) {
|
|
|
-
|
|
|
- return dateFormat
|
|
|
- .replace(yearRE, match => Number(date.getFullYear().toString().slice(-match.length)))
|
|
|
- .replace(monthRE, match => paddNum(date.getMonth() + 1, match.length))
|
|
|
- .replace(dayRE, match => paddNum(date.getDate(), match.length))
|
|
|
- .replace(hoursRE, match => paddNum(
|
|
|
- AMPMClockRE.test(dateFormat) ? to12HourClock(date.getHours()) : date.getHours(),
|
|
|
- match.length
|
|
|
- ))
|
|
|
- .replace(minutesRE, match => paddNum(date.getMinutes(), match.length))
|
|
|
- .replace(secondsRE, match => paddNum(date.getSeconds(), match.length))
|
|
|
- .replace(AMPMClockRE, match => isPM(date.getHours()) ? 'PM' : 'AM')
|
|
|
- ;
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- incrementMonth(increment = 1) {
|
|
|
-
|
|
|
- const refDate = new Date(this.currentPeriod.year, this.currentPeriod.month);
|
|
|
- const incrementDate = new Date(refDate.getFullYear(), refDate.getMonth() + increment);
|
|
|
-
|
|
|
- this.currentPeriod = {
|
|
|
- month: incrementDate.getMonth(),
|
|
|
- year: incrementDate.getFullYear()
|
|
|
- };
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- processUserInput(userText) {
|
|
|
-
|
|
|
- const userDate = this.parseDateString(
|
|
|
- userText, this.displayFormat || this.format
|
|
|
- );
|
|
|
-
|
|
|
- this.inputValue = userText;
|
|
|
-
|
|
|
- this.$emit('input', userDate
|
|
|
- ? this.formatDateToString(userDate, this.format)
|
|
|
- : userText
|
|
|
- );
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- toggle() {
|
|
|
-
|
|
|
- return this.opened ? this.close() : this.open();
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- open() {
|
|
|
-
|
|
|
- if (!this.opened) {
|
|
|
- this.opened = true;
|
|
|
- this.currentPeriod = this.startPeriod || this.getPeriodFromValue(
|
|
|
- this.value, this.format
|
|
|
- );
|
|
|
- this.addCloseEvents();
|
|
|
- this.setupPosition();
|
|
|
- }
|
|
|
- this.direction = undefined;
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- close() {
|
|
|
-
|
|
|
- if (this.opened) {
|
|
|
- this.opened = false;
|
|
|
- this.direction = undefined;
|
|
|
- this.removeCloseEvents();
|
|
|
- this.teardownPosition();
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- closeViaOverlay(e) {
|
|
|
-
|
|
|
- if (this.hasInputElement && e.target === this.$refs.outerWrap) {
|
|
|
- this.close();
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- addCloseEvents() {
|
|
|
-
|
|
|
- if (!this.closeEventListener) {
|
|
|
-
|
|
|
- this.closeEventListener = e => this.inspectCloseEvent(e);
|
|
|
-
|
|
|
- ['click', 'keyup', 'focusin'].forEach(
|
|
|
- eventName => document.addEventListener(eventName, this.closeEventListener)
|
|
|
- );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- inspectCloseEvent(event) {
|
|
|
-
|
|
|
- if (event.keyCode) {
|
|
|
- event.keyCode === 27 && this.close();
|
|
|
- } else if (!(event.target === this.$el) && !this.$el.contains(event.target)) {
|
|
|
- this.close();
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- removeCloseEvents() {
|
|
|
-
|
|
|
- if (this.closeEventListener) {
|
|
|
-
|
|
|
- ['click', 'keyup', 'focusin'].forEach(
|
|
|
- eventName => document.removeEventListener(eventName, this.closeEventListener)
|
|
|
- );
|
|
|
-
|
|
|
- delete this.closeEventListener;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- setupPosition() {
|
|
|
-
|
|
|
- if (!this.positionEventListener) {
|
|
|
- this.positionEventListener = () => this.positionFloater();
|
|
|
- window.addEventListener('resize', this.positionEventListener);
|
|
|
- }
|
|
|
-
|
|
|
- this.positionFloater();
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- positionFloater() {
|
|
|
-
|
|
|
- const inputRect = this.$el.getBoundingClientRect();
|
|
|
-
|
|
|
- let verticalClass = 'vdpPositionTop';
|
|
|
- let horizontalClass = 'vdpPositionLeft';
|
|
|
-
|
|
|
- const calculate = () => {
|
|
|
-
|
|
|
- const rect = this.$refs.outerWrap.getBoundingClientRect();
|
|
|
- const floaterHeight = rect.height;
|
|
|
- const floaterWidth = rect.width;
|
|
|
-
|
|
|
- if (window.innerWidth > this.mobileBreakpointWidth) {
|
|
|
-
|
|
|
- // vertical
|
|
|
- if (
|
|
|
- (inputRect.top + inputRect.height + floaterHeight > window.innerHeight) &&
|
|
|
- (inputRect.top - floaterHeight > 0)
|
|
|
- ) {
|
|
|
- verticalClass = 'vdpPositionBottom';
|
|
|
- }
|
|
|
-
|
|
|
- // horizontal
|
|
|
- if (inputRect.left + floaterWidth > window.innerWidth) {
|
|
|
- horizontalClass = 'vdpPositionRight';
|
|
|
- }
|
|
|
-
|
|
|
- this.positionClass = ['vdpPositionReady', verticalClass, horizontalClass].join(' ');
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- this.positionClass = 'vdpPositionFixed';
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- };
|
|
|
-
|
|
|
- this.$refs.outerWrap ? calculate() : this.$nextTick(calculate);
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- teardownPosition() {
|
|
|
-
|
|
|
- if (this.positionEventListener) {
|
|
|
- this.positionClass = undefined;
|
|
|
- window.removeEventListener('resize', this.positionEventListener);
|
|
|
- delete this.positionEventListener;
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- clear() {
|
|
|
-
|
|
|
- this.$emit('input', '');
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- selectDateItem(item) {
|
|
|
-
|
|
|
- if (!item.disabled) {
|
|
|
-
|
|
|
- const newDate = new Date(item.date);
|
|
|
-
|
|
|
- if (this.currentTime) {
|
|
|
- newDate.setHours(this.currentTime.hours);
|
|
|
- newDate.setMinutes(this.currentTime.minutes);
|
|
|
- newDate.setSeconds(this.currentTime.seconds);
|
|
|
- }
|
|
|
-
|
|
|
- this.$emit('input', this.formatDateToString(newDate, this.format));
|
|
|
-
|
|
|
- if (this.hasInputElement && !this.pickTime) {
|
|
|
- this.close();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- set12HourClock(value) {
|
|
|
-
|
|
|
- const currentDate = new Date(this.valueDate);
|
|
|
- const currentHours = currentDate.getHours();
|
|
|
-
|
|
|
- currentDate.setHours(value === 'PM'
|
|
|
- ? currentHours + 12
|
|
|
- : currentHours - 12
|
|
|
- );
|
|
|
-
|
|
|
- this.$emit('input', this.formatDateToString(currentDate, this.format));
|
|
|
- },
|
|
|
-
|
|
|
- inputHours(event) {
|
|
|
-
|
|
|
- const currentDate = new Date(this.valueDate);
|
|
|
- const currentHours = currentDate.getHours();
|
|
|
- const targetValue = parseInt(event.target.value, 10) || 0;
|
|
|
- const minHours = this.use12HourClock ? 1 : 0;
|
|
|
- const maxHours = this.use12HourClock ? 12 : 23;
|
|
|
- const numValue = boundNumber(targetValue, minHours, maxHours);
|
|
|
-
|
|
|
- currentDate.setHours(this.use12HourClock
|
|
|
- ? to24HourClock(numValue, isPM(currentHours))
|
|
|
- : numValue
|
|
|
- );
|
|
|
- event.target.value = paddNum(numValue, 1);
|
|
|
- this.$emit('input', this.formatDateToString(currentDate, this.format));
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- inputTime(method, event) {
|
|
|
-
|
|
|
- const currentDate = new Date(this.valueDate);
|
|
|
- const targetValue = parseInt(event.target.value) || 0;
|
|
|
- const numValue = boundNumber(targetValue, 0, 59);
|
|
|
-
|
|
|
- event.target.value = paddNum(numValue, 2);
|
|
|
- currentDate[method](numValue);
|
|
|
-
|
|
|
- this.$emit('input', this.formatDateToString(currentDate, this.format));
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- onTimeInputFocus(event) {
|
|
|
- event.target.select && event.target.select();
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
-};
|
|
|
-
|
|
|
-function paddNum(num, padsize) {
|
|
|
-
|
|
|
- return typeof num !== 'undefined'
|
|
|
- ? num.toString().length > padsize
|
|
|
- ? num
|
|
|
- : new Array(padsize - num.toString().length + 1).join('0') + num
|
|
|
- : undefined
|
|
|
- ;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-function chunkArray(inputArray, chunkSize) {
|
|
|
-
|
|
|
- const results = [];
|
|
|
-
|
|
|
- while (inputArray.length) {
|
|
|
- results.push(inputArray.splice(0, chunkSize));
|
|
|
- }
|
|
|
-
|
|
|
- return results;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-function areSameDates(date1, date2) {
|
|
|
-
|
|
|
- return (date1.getDate() === date2.getDate()) &&
|
|
|
- (date1.getMonth() === date2.getMonth()) &&
|
|
|
- (date1.getFullYear() === date2.getFullYear())
|
|
|
- ;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-function range(start, end) {
|
|
|
-
|
|
|
- const results = [];
|
|
|
-
|
|
|
- for (let i = start; i <= end; i++) {
|
|
|
- results.push(i);
|
|
|
- }
|
|
|
- return results;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-function to12HourClock(hours) {
|
|
|
-
|
|
|
- const remainder = hours % 12;
|
|
|
- return remainder === 0 ? 12 : remainder;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-function to24HourClock(hours, PM) {
|
|
|
-
|
|
|
- return PM
|
|
|
- ? (hours === 12 ? hours : hours + 12)
|
|
|
- : (hours === 12 ? 0 : hours)
|
|
|
- ;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-function isPM(hours) {
|
|
|
-
|
|
|
- return hours >= 12;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-function boundNumber(value, min, max) {
|
|
|
- return Math.min(Math.max(value, min), max);
|
|
|
-}
|
|
|
-
|
|
|
-</script>
|
|
|
-
|
|
|
-<style lang="scss">
|
|
|
-
|
|
|
- @import './vueDatePick.scss';
|
|
|
-
|
|
|
-</style>
|