Skip to content

Replace blank days with dates from previous / next months #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion docs/.vuepress/components/Demo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,20 @@
<datepicker placeholder="Select Date" first-day-of-week="mon"></datepicker>
</code>
<hr />
<p>{{ vModelExample }}</p>
</div>
</div>

<div class="example">
<h3>Only show dates from current month datepicker</h3>
<Datepicker
placeholder="Select Date"
:show-edge-dates="false"
/>
<div class="coding">
<code>
&lt;datepicker placeholder="Select Date" :show-edge-dates="false"&gt;&lt;/datepicker&gt;
</code>
<hr />
</div>
</div>

Expand Down
3 changes: 2 additions & 1 deletion docs/guide/Props/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
| calendar-button | Boolean | false | Show an icon that that can be clicked |
| calendar-button-icon | String | | Use icon for button (ex: fa fa-calendar) |
| calendar-button-icon-content | String | | Use for material-icons (ex: event) |
| calendar-class | String\|Object | | CSS class applied to the calendar el |
| calendar-class | String&#124;Object | | CSS class applied to the calendar el |
| clear-button | Boolean | false | Show an icon for clearing the date |
| clear-button-icon | String | | Use icon for button (ex: fa fa-times) |
| day-cell-content | Function | | Use to render custom content in day cell |
Expand All @@ -33,6 +33,7 @@
| placeholder | String | | Input placeholder text |
| required | Boolean | false | Sets html required attribute on input |
| ref-name | String | | Sets a ref name directly on the input field |
| show-edge-dates | Boolean | true | If `false`, dates from previous/next months won't show |
| show-header | Boolean | true | If `false`, the header section won't show |
| show-calendar-on-focus | Boolean | false | Opens the calendar on input focus |
| show-calendar-on-button-click | Boolean | false | Only open the calendar on calendar-button click |
Expand Down
11 changes: 11 additions & 0 deletions example/Demo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@
</code>
</div>

<div class="example">
<h3>Only show dates from current month datepicker</h3>
<Datepicker
placeholder="Type or select date"
:show-edge-dates="false"
/>
<code>
&lt;datepicker placeholder="Type or select date" :show-edge-dates="false"&gt;&lt;/datepicker&gt;
</code>
</div>

<div class="example">
<h3>Bootstrap styled datepicker</h3>
<Datepicker
Expand Down
5 changes: 5 additions & 0 deletions src/components/Datepicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
:page-date="pageDate"
:page-timestamp="pageTimestamp"
:selected-date="selectedDate"
:show-edge-dates="showEdgeDates"
:show-full-month-name="fullMonthName"
:show-header="showHeader"
:translation="translation"
Expand Down Expand Up @@ -202,6 +203,10 @@ export default {
type: String,
default: 'day',
},
showEdgeDates: {
type: Boolean,
default: true,
},
showHeader: {
type: Boolean,
default: true,
Expand Down
85 changes: 53 additions & 32 deletions src/components/PickerDay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,15 @@
>
{{ d }}
</span>
<template v-if="blankDays > 0">
<span
v-for="d in blankDays"
:key="d.timestamp"
class="cell day blank"
/>
<!-- TODO change grid system setup with for example flex to remove the magic -->
<!-- the comment arrows in the next two lines are necessary magic to remove the WS -->
</template><!--
--><span
v-for="day in days"
:key="day.timestamp"
:class="dayClasses(day)"
class="cell day"
@click="selectDate(day)"
>{{ dayCellContent(day) }}</span>
<span
v-for="day in days"
:key="day.timestamp"
class="cell day"
:class="dayClasses(day)"
@click="selectDate(day)"
>
{{ dayCellContent(day) }}
</span>
</div>
<slot name="calendarFooterDay" />
</div>
Expand Down Expand Up @@ -78,17 +71,12 @@ export default {
type: Boolean,
default: false,
},
showEdgeDates: {
type: Boolean,
default: true,
},
},
computed: {
/**
* Returns the day number of the week less one for the first of the current month
* Used to show amount of empty cells before the first in the day calendar layout
* @return {Number}
*/
blankDays() {
const dObj = this.newPageDate()
return (7 - this.firstDayOfWeekNumber + this.utils.getDay(dObj)) % 7
},
/**
* Gets the name of the month the current page is on
* @return {String}
Expand All @@ -112,11 +100,10 @@ export default {
*/
days() {
const days = []
const dObj = this.newPageDate()
const daysInMonth = this.utils.daysInMonth(
this.utils.getFullYear(dObj), this.utils.getMonth(dObj),
)
for (let i = 0; i < daysInMonth; i += 1) {
const daysInCalendar = this.daysFromPrevMonth + this.daysInMonth + this.daysFromNextMonth
const firstOfMonth = this.newPageDate()
const dObj = new Date(firstOfMonth.setDate(firstOfMonth.getDate() - this.daysFromPrevMonth))
for (let i = 0; i < daysInCalendar; i += 1) {
days.push(this.makeDay(i, dObj))
this.utils.setDate(dObj, this.utils.getDate(dObj) + 1)
}
Expand All @@ -129,6 +116,30 @@ export default {
daysOfWeek() {
return this.translation.getDaysStartingOn(this.firstDayOfWeekNumber)
},
/**
* Returns the number of days in this month
* @return {String[]}
*/
daysInMonth() {
const dObj = this.newPageDate()
return this.utils.getDaysInMonth(dObj)
},
/**
* Calculates how many days to show from the previous month
* @return {number}
*/
daysFromPrevMonth() {
const dObj = this.newPageDate()
return (7 - this.firstDayOfWeekNumber + this.utils.getDay(dObj)) % 7
},
/**
* Calculates how many days to show from the next month
* @return {number}
*/
daysFromNextMonth() {
const daysThisAndPrevMonth = this.daysFromPrevMonth + this.daysInMonth
return (Math.ceil(daysThisAndPrevMonth / 7) * 7) - daysThisAndPrevMonth
},
/**
* Returns first-day-of-week as a number (Sunday is 0)
* @return {Number}
Expand Down Expand Up @@ -167,6 +178,10 @@ export default {
isYmd() {
return this.translation.ymd && this.translation.ymd === true
},
nextPageDate() {
const d = new Date(this.pageTimestamp)
return new Date(this.utils.setMonth(d, this.utils.getMonth(d) + 1))
},
},
methods: {
/**
Expand All @@ -188,6 +203,7 @@ export default {
selected: day.isSelected,
disabled: day.isDisabled,
highlighted: day.isHighlighted,
muted: day.isPreviousMonth || day.isNextMonth,
today: day.isToday,
weekend: day.isWeekend,
sat: day.isSaturday,
Expand Down Expand Up @@ -316,21 +332,26 @@ export default {
* @return {Object}
*/
makeDay(id, dObj) {
const isNextMonth = dObj >= this.nextPageDate
const isPreviousMonth = dObj < this.pageDate
const isSaturday = this.utils.getDay(dObj) === 6
const isSunday = this.utils.getDay(dObj) === 0
const showDate = this.showEdgeDates || !(isPreviousMonth || isNextMonth)

return {
date: this.utils.getDate(dObj),
date: showDate ? this.utils.getDate(dObj) : '',
timestamp: dObj.valueOf(),
isSelected: this.isSelectedDate(dObj),
isDisabled: this.isDisabledDate(dObj),
isDisabled: showDate ? this.isDisabledDate(dObj) : true,
isHighlighted: this.isHighlightedDate(dObj),
isHighlightStart: this.isHighlightStart(dObj),
isHighlightEnd: this.isHighlightEnd(dObj),
isToday: this.utils.compareDates(dObj, new Date()),
isWeekend: isSaturday || isSunday,
isSaturday,
isSunday,
isPreviousMonth,
isNextMonth,
}
},
/**
Expand Down
5 changes: 2 additions & 3 deletions src/styles/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@
}
}

&.grey {
color: #888;
&.muted {
color: #a3a3a3;

&:hover {
background: inherit;
Expand Down Expand Up @@ -175,7 +175,6 @@
}
}


.vdp-datepicker__clear-button,
.vdp-datepicker__calendar-button {
cursor: pointer;
Expand Down
8 changes: 8 additions & 0 deletions src/utils/DateUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ const utils = {
return this.useUtc ? date.getUTCMonth() : date.getMonth()
},

/**
* Returns the number of days in the month, using UTC or not
* @param {Date} date
*/
getDaysInMonth(date) {
return this.daysInMonth(this.getFullYear(date), this.getMonth(date))
},

/**
* Returns the date, using UTC or not
* @param {Date} date
Expand Down
51 changes: 39 additions & 12 deletions test/unit/specs/PickerDay/firstDayOfWeek.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { shallowMount } from '@vue/test-utils'
import PickerDay from '~/components/PickerDay'
import { en } from '~/locale'
import { makeDateUtils } from '~/utils/DateUtils'

const constructedDateUtils = makeDateUtils(false)

describe('PickerDay: Set first day of week', () => {
let wrapper
Expand All @@ -27,18 +30,30 @@ describe('PickerDay: Set first day of week', () => {
expect(wrapper.vm.daysOfWeek[6]).toEqual('Sun')
})

it('should have 6 blankDays when month starts on a Sunday', () => {
it('should have 6 days from previous month when month starts on a Sunday', () => {
const testDate = new Date(2020, 10, 1)
const startDate = constructedDateUtils.getNewDateObject(testDate)

wrapper.setProps({
pageDate: new Date(2018, 3, 1),
pageDate: testDate,
pageTimestamp: constructedDateUtils.setDate(startDate, 1),
})
expect(wrapper.vm.blankDays).toEqual(6)
for (let i = 0; i < 6; i += 1) {
expect(wrapper.vm.days[i].isPreviousMonth).toBeTruthy()
expect(wrapper.vm.days[i].isNextMonth).toBeFalsy()
}
})

it('should have no blankDays when month starts on a Monday', () => {
it('should have no days from previous month when month starts on a Monday', () => {
const testDate = new Date(2020, 5, 1)
const startDate = constructedDateUtils.getNewDateObject(testDate)

wrapper.setProps({
pageDate: new Date(2018, 9, 1),
pageDate: testDate,
pageTimestamp: constructedDateUtils.setDate(startDate, 1),
})
expect(wrapper.vm.blankDays).toEqual(0)
expect(wrapper.vm.days[0].isPreviousMonth).toBeFalsy()
expect(wrapper.vm.days[0].isNextMonth).toBeFalsy()
})
})

Expand All @@ -59,17 +74,29 @@ describe('PickerDay: Datepicker with Saturday as first day of week', () => {
wrapper.destroy()
})

it('should have 6 blankDays when month starts on a Friday', () => {
it('should have 6 days from previous month when month starts on a Friday', () => {
const testDate = new Date(2021, 0, 1)
const startDate = constructedDateUtils.getNewDateObject(testDate)

wrapper.setProps({
pageDate: new Date(2021, 0, 1),
pageDate: testDate,
pageTimestamp: constructedDateUtils.setDate(startDate, 1),
})
expect(wrapper.vm.blankDays).toEqual(6)
for (let i = 0; i < 6; i += 1) {
expect(wrapper.vm.days[i].isPreviousMonth).toBeTruthy()
expect(wrapper.vm.days[i].isNextMonth).toBeFalsy()
}
})

it('should have no blankDays when month starts on a Saturday', () => {
it('should have no days from previous month when month starts on a Saturday', () => {
const testDate = new Date(2020, 7, 1)
const startDate = constructedDateUtils.getNewDateObject(testDate)

wrapper.setProps({
pageDate: new Date(2020, 7, 1),
pageDate: testDate,
pageTimestamp: constructedDateUtils.setDate(startDate, 1),
})
expect(wrapper.vm.blankDays).toEqual(0)
expect(wrapper.vm.days[0].isPreviousMonth).toBeFalsy()
expect(wrapper.vm.days[0].isNextMonth).toBeFalsy()
})
})