Skip to content

Commit b14b611

Browse files
authored
feat(picker): Replace blank days with dates from previous / next months (sumcumo#46)
1 parent 06d3d08 commit b14b611

File tree

8 files changed

+134
-49
lines changed

8 files changed

+134
-49
lines changed

docs/.vuepress/components/Demo.vue

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,20 @@
3636
first-day-of-week="mon"></datepicker>
3737
</code>
3838
<hr />
39-
<p>{{ vModelExample }}</p>
39+
</div>
40+
</div>
41+
42+
<div class="example">
43+
<h3>Only show dates from current month datepicker</h3>
44+
<Datepicker
45+
placeholder="Select Date"
46+
:show-edge-dates="false"
47+
/>
48+
<div class="coding">
49+
<code>
50+
&lt;datepicker placeholder="Select Date" :show-edge-dates="false"&gt;&lt;/datepicker&gt;
51+
</code>
52+
<hr />
4053
</div>
4154
</div>
4255

docs/guide/Props/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
| calendar-button | Boolean | false | Show an icon that that can be clicked |
1010
| calendar-button-icon | String | | Use icon for button (ex: fa fa-calendar) |
1111
| calendar-button-icon-content | String | | Use for material-icons (ex: event) |
12-
| calendar-class | String\|Object | | CSS class applied to the calendar el |
12+
| calendar-class | String&#124;Object | | CSS class applied to the calendar el |
1313
| clear-button | Boolean | false | Show an icon for clearing the date |
1414
| clear-button-icon | String | | Use icon for button (ex: fa fa-times) |
1515
| day-cell-content | Function | | Use to render custom content in day cell |
@@ -33,6 +33,7 @@
3333
| placeholder | String | | Input placeholder text |
3434
| required | Boolean | false | Sets html required attribute on input |
3535
| ref-name | String | | Sets a ref name directly on the input field |
36+
| show-edge-dates | Boolean | true | If `false`, dates from previous/next months won't show |
3637
| show-header | Boolean | true | If `false`, the header section won't show |
3738
| show-calendar-on-focus | Boolean | false | Opens the calendar on input focus |
3839
| show-calendar-on-button-click | Boolean | false | Only open the calendar on calendar-button click |

example/Demo.vue

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,17 @@
3636
</code>
3737
</div>
3838

39+
<div class="example">
40+
<h3>Only show dates from current month datepicker</h3>
41+
<Datepicker
42+
placeholder="Type or select date"
43+
:show-edge-dates="false"
44+
/>
45+
<code>
46+
&lt;datepicker placeholder="Type or select date" :show-edge-dates="false"&gt;&lt;/datepicker&gt;
47+
</code>
48+
</div>
49+
3950
<div class="example">
4051
<h3>Bootstrap styled datepicker</h3>
4152
<Datepicker

src/components/Datepicker.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
:page-date="pageDate"
7777
:page-timestamp="pageTimestamp"
7878
:selected-date="selectedDate"
79+
:show-edge-dates="showEdgeDates"
7980
:show-full-month-name="fullMonthName"
8081
:show-header="showHeader"
8182
:translation="translation"
@@ -202,6 +203,10 @@ export default {
202203
type: String,
203204
default: 'day',
204205
},
206+
showEdgeDates: {
207+
type: Boolean,
208+
default: true,
209+
},
205210
showHeader: {
206211
type: Boolean,
207212
default: true,

src/components/PickerDay.vue

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,15 @@
3030
>
3131
{{ d }}
3232
</span>
33-
<template v-if="blankDays > 0">
34-
<span
35-
v-for="d in blankDays"
36-
:key="d.timestamp"
37-
class="cell day blank"
38-
/>
39-
<!-- TODO change grid system setup with for example flex to remove the magic -->
40-
<!-- the comment arrows in the next two lines are necessary magic to remove the WS -->
41-
</template><!--
42-
--><span
43-
v-for="day in days"
44-
:key="day.timestamp"
45-
:class="dayClasses(day)"
46-
class="cell day"
47-
@click="selectDate(day)"
48-
>{{ dayCellContent(day) }}</span>
33+
<span
34+
v-for="day in days"
35+
:key="day.timestamp"
36+
class="cell day"
37+
:class="dayClasses(day)"
38+
@click="selectDate(day)"
39+
>
40+
{{ dayCellContent(day) }}
41+
</span>
4942
</div>
5043
<slot name="calendarFooterDay" />
5144
</div>
@@ -78,17 +71,12 @@ export default {
7871
type: Boolean,
7972
default: false,
8073
},
74+
showEdgeDates: {
75+
type: Boolean,
76+
default: true,
77+
},
8178
},
8279
computed: {
83-
/**
84-
* Returns the day number of the week less one for the first of the current month
85-
* Used to show amount of empty cells before the first in the day calendar layout
86-
* @return {Number}
87-
*/
88-
blankDays() {
89-
const dObj = this.newPageDate()
90-
return (7 - this.firstDayOfWeekNumber + this.utils.getDay(dObj)) % 7
91-
},
9280
/**
9381
* Gets the name of the month the current page is on
9482
* @return {String}
@@ -112,11 +100,10 @@ export default {
112100
*/
113101
days() {
114102
const days = []
115-
const dObj = this.newPageDate()
116-
const daysInMonth = this.utils.daysInMonth(
117-
this.utils.getFullYear(dObj), this.utils.getMonth(dObj),
118-
)
119-
for (let i = 0; i < daysInMonth; i += 1) {
103+
const daysInCalendar = this.daysFromPrevMonth + this.daysInMonth + this.daysFromNextMonth
104+
const firstOfMonth = this.newPageDate()
105+
const dObj = new Date(firstOfMonth.setDate(firstOfMonth.getDate() - this.daysFromPrevMonth))
106+
for (let i = 0; i < daysInCalendar; i += 1) {
120107
days.push(this.makeDay(i, dObj))
121108
this.utils.setDate(dObj, this.utils.getDate(dObj) + 1)
122109
}
@@ -129,6 +116,30 @@ export default {
129116
daysOfWeek() {
130117
return this.translation.getDaysStartingOn(this.firstDayOfWeekNumber)
131118
},
119+
/**
120+
* Returns the number of days in this month
121+
* @return {String[]}
122+
*/
123+
daysInMonth() {
124+
const dObj = this.newPageDate()
125+
return this.utils.getDaysInMonth(dObj)
126+
},
127+
/**
128+
* Calculates how many days to show from the previous month
129+
* @return {number}
130+
*/
131+
daysFromPrevMonth() {
132+
const dObj = this.newPageDate()
133+
return (7 - this.firstDayOfWeekNumber + this.utils.getDay(dObj)) % 7
134+
},
135+
/**
136+
* Calculates how many days to show from the next month
137+
* @return {number}
138+
*/
139+
daysFromNextMonth() {
140+
const daysThisAndPrevMonth = this.daysFromPrevMonth + this.daysInMonth
141+
return (Math.ceil(daysThisAndPrevMonth / 7) * 7) - daysThisAndPrevMonth
142+
},
132143
/**
133144
* Returns first-day-of-week as a number (Sunday is 0)
134145
* @return {Number}
@@ -167,6 +178,10 @@ export default {
167178
isYmd() {
168179
return this.translation.ymd && this.translation.ymd === true
169180
},
181+
nextPageDate() {
182+
const d = new Date(this.pageTimestamp)
183+
return new Date(this.utils.setMonth(d, this.utils.getMonth(d) + 1))
184+
},
170185
},
171186
methods: {
172187
/**
@@ -188,6 +203,7 @@ export default {
188203
selected: day.isSelected,
189204
disabled: day.isDisabled,
190205
highlighted: day.isHighlighted,
206+
muted: day.isPreviousMonth || day.isNextMonth,
191207
today: day.isToday,
192208
weekend: day.isWeekend,
193209
sat: day.isSaturday,
@@ -316,21 +332,26 @@ export default {
316332
* @return {Object}
317333
*/
318334
makeDay(id, dObj) {
335+
const isNextMonth = dObj >= this.nextPageDate
336+
const isPreviousMonth = dObj < this.pageDate
319337
const isSaturday = this.utils.getDay(dObj) === 6
320338
const isSunday = this.utils.getDay(dObj) === 0
339+
const showDate = this.showEdgeDates || !(isPreviousMonth || isNextMonth)
321340
322341
return {
323-
date: this.utils.getDate(dObj),
342+
date: showDate ? this.utils.getDate(dObj) : '',
324343
timestamp: dObj.valueOf(),
325344
isSelected: this.isSelectedDate(dObj),
326-
isDisabled: this.isDisabledDate(dObj),
345+
isDisabled: showDate ? this.isDisabledDate(dObj) : true,
327346
isHighlighted: this.isHighlightedDate(dObj),
328347
isHighlightStart: this.isHighlightStart(dObj),
329348
isHighlightEnd: this.isHighlightEnd(dObj),
330349
isToday: this.utils.compareDates(dObj, new Date()),
331350
isWeekend: isSaturday || isSunday,
332351
isSaturday,
333352
isSunday,
353+
isPreviousMonth,
354+
isNextMonth,
334355
}
335356
},
336357
/**

src/styles/style.scss

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@
146146
}
147147
}
148148

149-
&.grey {
150-
color: #888;
149+
&.muted {
150+
color: #a3a3a3;
151151

152152
&:hover {
153153
background: inherit;
@@ -175,7 +175,6 @@
175175
}
176176
}
177177

178-
179178
.vdp-datepicker__clear-button,
180179
.vdp-datepicker__calendar-button {
181180
cursor: pointer;

src/utils/DateUtils.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ const utils = {
2121
return this.useUtc ? date.getUTCMonth() : date.getMonth()
2222
},
2323

24+
/**
25+
* Returns the number of days in the month, using UTC or not
26+
* @param {Date} date
27+
*/
28+
getDaysInMonth(date) {
29+
return this.daysInMonth(this.getFullYear(date), this.getMonth(date))
30+
},
31+
2432
/**
2533
* Returns the date, using UTC or not
2634
* @param {Date} date

test/unit/specs/PickerDay/firstDayOfWeek.spec.js

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { shallowMount } from '@vue/test-utils'
22
import PickerDay from '~/components/PickerDay'
33
import { en } from '~/locale'
4+
import { makeDateUtils } from '~/utils/DateUtils'
5+
6+
const constructedDateUtils = makeDateUtils(false)
47

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

30-
it('should have 6 blankDays when month starts on a Sunday', () => {
33+
it('should have 6 days from previous month when month starts on a Sunday', () => {
34+
const testDate = new Date(2020, 10, 1)
35+
const startDate = constructedDateUtils.getNewDateObject(testDate)
36+
3137
wrapper.setProps({
32-
pageDate: new Date(2018, 3, 1),
38+
pageDate: testDate,
39+
pageTimestamp: constructedDateUtils.setDate(startDate, 1),
3340
})
34-
expect(wrapper.vm.blankDays).toEqual(6)
41+
for (let i = 0; i < 6; i += 1) {
42+
expect(wrapper.vm.days[i].isPreviousMonth).toBeTruthy()
43+
expect(wrapper.vm.days[i].isNextMonth).toBeFalsy()
44+
}
3545
})
3646

37-
it('should have no blankDays when month starts on a Monday', () => {
47+
it('should have no days from previous month when month starts on a Monday', () => {
48+
const testDate = new Date(2020, 5, 1)
49+
const startDate = constructedDateUtils.getNewDateObject(testDate)
50+
3851
wrapper.setProps({
39-
pageDate: new Date(2018, 9, 1),
52+
pageDate: testDate,
53+
pageTimestamp: constructedDateUtils.setDate(startDate, 1),
4054
})
41-
expect(wrapper.vm.blankDays).toEqual(0)
55+
expect(wrapper.vm.days[0].isPreviousMonth).toBeFalsy()
56+
expect(wrapper.vm.days[0].isNextMonth).toBeFalsy()
4257
})
4358
})
4459

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

62-
it('should have 6 blankDays when month starts on a Friday', () => {
77+
it('should have 6 days from previous month when month starts on a Friday', () => {
78+
const testDate = new Date(2021, 0, 1)
79+
const startDate = constructedDateUtils.getNewDateObject(testDate)
80+
6381
wrapper.setProps({
64-
pageDate: new Date(2021, 0, 1),
82+
pageDate: testDate,
83+
pageTimestamp: constructedDateUtils.setDate(startDate, 1),
6584
})
66-
expect(wrapper.vm.blankDays).toEqual(6)
85+
for (let i = 0; i < 6; i += 1) {
86+
expect(wrapper.vm.days[i].isPreviousMonth).toBeTruthy()
87+
expect(wrapper.vm.days[i].isNextMonth).toBeFalsy()
88+
}
6789
})
6890

69-
it('should have no blankDays when month starts on a Saturday', () => {
91+
it('should have no days from previous month when month starts on a Saturday', () => {
92+
const testDate = new Date(2020, 7, 1)
93+
const startDate = constructedDateUtils.getNewDateObject(testDate)
94+
7095
wrapper.setProps({
71-
pageDate: new Date(2020, 7, 1),
96+
pageDate: testDate,
97+
pageTimestamp: constructedDateUtils.setDate(startDate, 1),
7298
})
73-
expect(wrapper.vm.blankDays).toEqual(0)
99+
expect(wrapper.vm.days[0].isPreviousMonth).toBeFalsy()
100+
expect(wrapper.vm.days[0].isNextMonth).toBeFalsy()
74101
})
75102
})

0 commit comments

Comments
 (0)