Skip to content

Commit 618e3d3

Browse files
committed
feat(pickers): Add slide transition on page change
1 parent f841f4d commit 618e3d3

17 files changed

+191
-73
lines changed

src/components/Datepicker.vue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,15 @@
7272
:show-edge-dates="showEdgeDates"
7373
:show-full-month-name="fullMonthName"
7474
:show-header="showHeader"
75+
:transition-name="transitionName"
7576
:translation="translation"
7677
:use-utc="useUtc"
7778
:view="view || computedInitialView"
7879
:year-range="yearPickerRange"
7980
@page-change="handlePageChange"
8081
@select="handleSelect"
8182
@select-disabled="handleSelectDisabled"
83+
@set-transition-name="setTransitionName($event)"
8284
@set-view="setView"
8385
>
8486
<template v-for="slotKey of calendarSlots">
@@ -226,6 +228,7 @@ export default {
226228
* {Date}
227229
*/
228230
selectedDate: null,
231+
transitionName: '',
229232
utils,
230233
view: '',
231234
}
@@ -477,6 +480,19 @@ export default {
477480
}
478481
this.pageTimestamp = this.utils.setDate(new Date(dateTemp), 1)
479482
},
483+
/**
484+
* Sets the direction of the slide transition
485+
* @param {Number} plusOrMinus Positive for the future; negative for the past
486+
*/
487+
setTransitionName(plusOrMinus) {
488+
const isInTheFuture = plusOrMinus > 0
489+
490+
if (this.isRtl) {
491+
this.transitionName = isInTheFuture ? 'slide-left' : 'slide-right'
492+
} else {
493+
this.transitionName = isInTheFuture ? 'slide-right' : 'slide-left'
494+
}
495+
},
480496
/**
481497
* Set the datepicker value
482498
* @param {Date|String|Number|null} date

src/components/PickerCells.vue

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div>
2+
<div class="picker-cells">
33
<span
44
v-for="cell in cells"
55
:key="cell.timestamp"
@@ -23,12 +23,37 @@ export default {
2323
type: Boolean,
2424
default: true,
2525
},
26+
transitionName: {
27+
type: String,
28+
default: '',
29+
},
2630
view: {
2731
type: String,
2832
validator: (val) => ['day', 'month', 'year'].includes(val),
2933
required: true,
3034
},
3135
},
36+
data() {
37+
return {
38+
cellHeight: 0,
39+
}
40+
},
41+
computed: {
42+
wrapperHeight() {
43+
const columns = this.view === 'day' ? 7 : 3
44+
const rows = Math.ceil(this.cells.length / columns)
45+
46+
return rows * this.cellHeight
47+
},
48+
},
49+
watch: {
50+
wrapperHeight() {
51+
this.$parent.$refs.cellsWrapper.style.height = `${this.wrapperHeight}px`
52+
},
53+
},
54+
mounted() {
55+
this.cellHeight = this.getCellHeight()
56+
},
3257
methods: {
3358
/**
3459
* Set the classes for a specific cell
@@ -57,6 +82,22 @@ export default {
5782
},
5883
]
5984
},
85+
86+
/**
87+
* Get the cell height
88+
*/
89+
/* eslint no-param-reassign: 0 */
90+
getCellHeight() {
91+
const popup = this.$parent.$parent.$el
92+
const originalDisplay = popup.style.display
93+
const originalVisibility = popup.style.visibility
94+
popup.style.display = 'block'
95+
popup.style.visibility = 'hidden'
96+
const height = this.$el.children[0].offsetHeight
97+
popup.style.display = originalDisplay
98+
popup.style.visibility = originalVisibility
99+
return height
100+
},
60101
},
61102
}
62103
</script>

src/components/PickerDay.vue

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,21 @@
2727
</span>
2828
</div>
2929

30-
<PickerCells
31-
ref="cells"
32-
:key="pageTitleDay"
33-
v-slot="{ cell }"
34-
:cells="cells"
35-
:show-edge-dates="showEdgeDates"
36-
view="day"
37-
@select="select($event)"
38-
>
39-
{{ dayCellContent(cell) }}
40-
</PickerCells>
30+
<div ref="cellsWrapper" class="cells-wrapper">
31+
<Transition :name="transitionName">
32+
<PickerCells
33+
ref="cells"
34+
:key="pageTitleDay"
35+
v-slot="{ cell }"
36+
:cells="cells"
37+
:show-edge-dates="showEdgeDates"
38+
view="day"
39+
@select="select($event)"
40+
>
41+
{{ dayCellContent(cell) }}
42+
</PickerCells>
43+
</Transition>
44+
</div>
4145
</div>
4246

4347
<slot name="calendarFooterDay" />

src/components/PickerMonth.vue

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,20 @@
2020
<slot slot="nextIntervalBtn" name="nextIntervalBtn" />
2121
</PickerHeader>
2222

23-
<PickerCells
24-
ref="cells"
25-
:key="pageTitleMonth"
26-
v-slot="{ cell }"
27-
:cells="cells"
28-
view="month"
29-
@select="select($event)"
30-
>
31-
{{ cell.month }}
32-
</PickerCells>
23+
<div ref="cellsWrapper" class="cells-wrapper">
24+
<Transition :name="transitionName">
25+
<PickerCells
26+
ref="cells"
27+
:key="pageTitleMonth"
28+
v-slot="{ cell }"
29+
:cells="cells"
30+
view="month"
31+
@select="select($event)"
32+
>
33+
{{ cell.month }}
34+
</PickerCells>
35+
</Transition>
36+
</div>
3337

3438
<slot name="calendarFooterMonth" />
3539
</div>

src/components/PickerYear.vue

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,20 @@
1616
<slot slot="nextIntervalBtn" name="nextIntervalBtn" />
1717
</PickerHeader>
1818

19-
<PickerCells
20-
ref="cells"
21-
:key="pageTitleYear"
22-
v-slot="{ cell }"
23-
:cells="cells"
24-
view="year"
25-
@select="select($event)"
26-
>
27-
{{ cell.year }}
28-
</PickerCells>
19+
<div ref="cellsWrapper" class="cells-wrapper">
20+
<Transition :name="transitionName">
21+
<PickerCells
22+
ref="cells"
23+
:key="pageTitleYear"
24+
v-slot="{ cell }"
25+
:cells="cells"
26+
view="year"
27+
@select="select($event)"
28+
>
29+
{{ cell.year }}
30+
</PickerCells>
31+
</Transition>
32+
</div>
2933

3034
<slot name="calendarFooterYear" />
3135
</div>

src/mixins/pickerMixin.vue

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export default {
3333
type: Boolean,
3434
default: true,
3535
},
36+
transitionName: {
37+
type: String,
38+
default: '',
39+
},
3640
translation: {
3741
type: Object,
3842
default() {
@@ -79,6 +83,8 @@ export default {
7983
const units =
8084
this.view === 'year' ? incrementBy * this.yearRange : incrementBy
8185
86+
this.$emit('set-transition-name', incrementBy)
87+
8288
if (this.view === 'day') {
8389
utils.setMonth(pageDate, utils.getMonth(pageDate) + units)
8490
} else {
@@ -88,15 +94,24 @@ export default {
8894
this.$emit('page-change', pageDate)
8995
},
9096
/**
91-
* Emits a 'select' or 'select-disabled' event
97+
* Determines which transition to use (for edge dates) and emits a 'select' or 'select-disabled' event
9298
* @param {Object} cell
9399
*/
94100
select(cell) {
95101
if (cell.isDisabled) {
96102
this.$emit('select-disabled', cell)
97-
} else {
98-
this.$emit('select', cell)
103+
return
99104
}
105+
106+
if (cell.isPreviousMonth) {
107+
this.$emit('set-transition-name', -1)
108+
}
109+
110+
if (cell.isNextMonth) {
111+
this.$emit('set-transition-name', 1)
112+
}
113+
114+
this.$emit('select', cell)
100115
},
101116
},
102117
}

src/styles/style.scss

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,42 @@
190190

191191
.picker-view {
192192
width: inherit;
193+
194+
.cells-wrapper {
195+
overflow: hidden;
196+
position: relative;
197+
transition-duration: 250ms;
198+
transition-timing-function: ease-in-out;
199+
200+
.picker-cells {
201+
transition-duration: 250ms;
202+
transition-timing-function: ease-in-out;
203+
}
204+
}
205+
206+
.slide-right-leave-active,
207+
.slide-right-enter-active {
208+
position: absolute;
209+
top: 0;
210+
}
211+
.slide-right-enter {
212+
transform: translate(100%, 0);
213+
}
214+
.slide-right-leave-to {
215+
transform: translate(-100%, 0);
216+
}
217+
218+
.slide-left-leave-active,
219+
.slide-left-enter-active {
220+
position: absolute;
221+
top: 0;
222+
}
223+
.slide-left-enter {
224+
transform: translate(-100%, 0);
225+
}
226+
.slide-left-leave-to {
227+
transform: translate(100%, 0);
228+
}
193229
}
194230
}
195231

test/unit/specs/Datepicker/Datepicker.spec.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,22 @@ describe('Datepicker shallowMounted', () => {
353353
expect(wrapperTemp.vm.selectedDate).toEqual(null)
354354
expect(wrapperTemp.emitted('input')).toBeTruthy()
355355
})
356+
357+
it('sets the transition correctly', () => {
358+
wrapper.vm.setTransitionName(1)
359+
expect(wrapper.vm.transitionName).toBe('slide-right')
360+
361+
wrapper.vm.setTransitionName(-1)
362+
expect(wrapper.vm.transitionName).toBe('slide-left')
363+
364+
wrapper.setData({ translation: { rtl: true } })
365+
366+
wrapper.vm.setTransitionName(1)
367+
expect(wrapper.vm.transitionName).toBe('slide-left')
368+
369+
wrapper.vm.setTransitionName(-1)
370+
expect(wrapper.vm.transitionName).toBe('slide-right')
371+
})
356372
})
357373

358374
describe('Datepicker.vue set by string', () => {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { shallowMount } from '@vue/test-utils'
1+
import { mount } from '@vue/test-utils'
22
import PickerDay from '~/components/PickerDay.vue'
33
import { en } from '~/locale'
44

55
describe('PickerDay: disabled', () => {
66
let wrapper
77

88
beforeEach(() => {
9-
wrapper = shallowMount(PickerDay, {
9+
wrapper = mount(PickerDay, {
1010
propsData: {
1111
translation: en,
1212
disabledDates: {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { shallowMount } from '@vue/test-utils'
1+
import { mount } from '@vue/test-utils'
22
import PickerDay from '~/components/PickerDay.vue'
33
import { en } from '~/locale'
44
import makeDateUtils from '~/utils/DateUtils'
@@ -9,7 +9,7 @@ describe('PickerDay: Set first day of week', () => {
99
let wrapper
1010

1111
beforeEach(() => {
12-
wrapper = shallowMount(PickerDay, {
12+
wrapper = mount(PickerDay, {
1313
propsData: {
1414
firstDayOfWeek: 'mon',
1515
translation: en,
@@ -61,7 +61,7 @@ describe('PickerDay: Datepicker with Saturday as first day of week', () => {
6161
let wrapper
6262

6363
beforeEach(() => {
64-
wrapper = shallowMount(PickerDay, {
64+
wrapper = mount(PickerDay, {
6565
propsData: {
6666
firstDayOfWeek: 'sat',
6767
translation: en,

0 commit comments

Comments
 (0)