Skip to content

Commit

Permalink
feat(picker): Replace blank days with dates from previous / next mont…
Browse files Browse the repository at this point in the history
…hs (#46)
  • Loading branch information
mst101 authored Oct 29, 2020
1 parent 06d3d08 commit b14b611
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 49 deletions.
15 changes: 14 additions & 1 deletion docs/.vuepress/components/Demo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,20 @@
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()
})
})

0 comments on commit b14b611

Please sign in to comment.