Skip to content

Commit 2cd5687

Browse files
authored
Merge pull request #3 from BatuhanTb/enhancement/arrow-select
Enhancement | Arrow buttons to ease selecting y/m/s
2 parents c1f8d60 + 8da13c5 commit 2cd5687

File tree

5 files changed

+256
-83
lines changed

5 files changed

+256
-83
lines changed

src/VueCtkDateTimePicker/_subs/PickersContainer/_subs/DatePicker/_subs/YearMonthSelector.vue

Lines changed: 146 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -3,110 +3,137 @@
33
class="year-month-selector flex flex-direction-column"
44
:class="{'dark': dark}"
55
>
6-
<div class="flex justify-content-right">
6+
<div class="year-month-selector__actions">
7+
<div v-if="!this.isMonthMode" class="chang-years-section">
8+
<v-b-arrow-button type="left" :color=color @click="handlePrevYearsClick()" />
9+
<v-b-arrow-button type="right" :color=color @click="handleNextYearsClick()" />
10+
</div>
11+
712
<CustomButton
813
:color="dark ? '#757575' : '#424242'"
914
:dark="dark"
1015
with-border
1116
@click="$emit('back')"
17+
class="back-button"
1218
>
1319
<span class="fs-16">
1420
1521
</span>
1622
</CustomButton>
1723
</div>
18-
<div class="flex-1 flex flex-wrap justify-content-between align-center">
19-
<CustomButton
20-
v-for="(m, index) in months"
21-
:key="index"
22-
:color="color"
23-
:selected="currentMonth === index"
24-
:dark="dark"
25-
class="month-button"
26-
with-border
27-
@click="selectMonth(index)"
28-
>
29-
{{ m }}
30-
</CustomButton>
31-
<CustomButton
32-
v-for="year in years"
33-
:key="year"
34-
:color="color"
35-
:dark="dark"
36-
:selected="currentYear === year"
37-
with-border
38-
@click="selectYear(year)"
39-
>
40-
{{ year }}
41-
</CustomButton>
42-
</div>
24+
<TransitionGroup :name="transitionYearsName" tag="div">
25+
<div :key="transitionKey" class="years-month-wrapper">
26+
<CustomButton
27+
v-for="(m, index) in months"
28+
:key="index"
29+
:color="color"
30+
:selected="currentMonth === index"
31+
:dark="dark"
32+
class="month-button"
33+
with-border
34+
@click="selectMonth(index)"
35+
>
36+
{{ m }}
37+
</CustomButton>
38+
<CustomButton
39+
v-for="year in years"
40+
:key="year"
41+
:color="color"
42+
:dark="dark"
43+
:selected="currentYear === year"
44+
with-border
45+
class="year-button"
46+
@click="selectYear(year)"
47+
>
48+
{{ year }}
49+
</CustomButton>
50+
</div>
51+
</TransitionGroup>
4352
</div>
4453
</template>
4554

4655
<script>
47-
import { getMonthsShort } from '@/VueCtkDateTimePicker/modules/month'
48-
import CustomButton from '@/VueCtkDateTimePicker/_subs/CustomButton'
56+
import { getMonthsShort } from '@/VueCtkDateTimePicker/modules/month'
57+
import CustomButton from '@/VueCtkDateTimePicker/_subs/CustomButton'
58+
import VBArrowButton from '@/components/arrow_button'
4959
50-
const ArrayRange = (start, end) => {
51-
return Array(end - start + 1).fill().map((_, idx) => {
52-
const n = start + idx
53-
return n
54-
})
55-
}
60+
const YEARS_ON_DISPLAY = 15
5661
57-
export default {
58-
name: 'YearMonthSelector',
59-
components: {
60-
CustomButton
61-
},
62-
props: {
63-
locale: { type: String, default: null },
64-
dark: { type: Boolean, default: null },
65-
color: { type: String, default: null },
66-
mode: { type: String, default: null },
67-
month: { type: Object, default: null }
68-
},
69-
data () {
70-
return {
71-
months: null,
72-
years: null
73-
}
74-
},
75-
computed: {
76-
currentMonth () {
77-
return this.month.month
62+
const ArrayRange = (start, end) => {
63+
return Array(end - start + 1).fill().map((_, idx) => {
64+
const n = start + idx
65+
return n
66+
})
67+
}
68+
69+
export default {
70+
name: 'YearMonthSelector',
71+
components: {
72+
CustomButton,
73+
VBArrowButton
7874
},
79-
currentYear () {
80-
return this.month.year
75+
props: {
76+
locale: { type: String, default: null },
77+
dark: { type: Boolean, default: null },
78+
color: { type: String, default: null },
79+
mode: { type: String, default: null },
80+
month: { type: Object, default: null }
8181
},
82-
isMonthMode () {
83-
return this.mode === 'month'
84-
}
85-
},
86-
mounted () {
87-
if (this.isMonthMode) {
88-
this.getMonths()
89-
} else {
90-
this.getYears()
91-
}
92-
},
93-
methods: {
94-
getMonths () {
95-
this.years = null
96-
this.months = getMonthsShort(this.locale)
82+
data () {
83+
return {
84+
months: null,
85+
years: null,
86+
transitionYearsName: 'slide-next',
87+
transitionKey: 0
88+
}
9789
},
98-
getYears () {
99-
this.months = null
100-
this.years = ArrayRange(this.month.year - 7, this.month.year + 7)
90+
computed: {
91+
currentMonth () {
92+
return this.month.month
93+
},
94+
currentYear () {
95+
return this.month.year
96+
},
97+
isMonthMode () {
98+
return this.mode === 'month'
99+
}
101100
},
102-
selectMonth (monthNumber) {
103-
this.$emit('input', { month: monthNumber, year: this.currentYear })
101+
mounted () {
102+
if (this.isMonthMode) {
103+
this.getMonths()
104+
} else {
105+
this.getYears(this.month.year - Math.floor(YEARS_ON_DISPLAY / 2))
106+
}
104107
},
105-
selectYear (year) {
106-
this.$emit('input', { month: this.currentMonth, year: year })
108+
methods: {
109+
getMonths () {
110+
this.years = null
111+
this.months = getMonthsShort(this.locale)
112+
},
113+
getYears (startYear) {
114+
this.months = null
115+
this.years = ArrayRange(startYear, startYear + YEARS_ON_DISPLAY - 1)
116+
},
117+
selectMonth (monthNumber) {
118+
this.$emit('input', { month: monthNumber, year: this.currentYear })
119+
},
120+
selectYear (year) {
121+
this.$emit('input', { month: this.currentMonth, year: year })
122+
},
123+
124+
handlePrevYearsClick() {
125+
this.transitionYearsName = 'slideprev'
126+
this.getYears(this.years[0] - YEARS_ON_DISPLAY)
127+
this.transitionKey++
128+
},
129+
130+
handleNextYearsClick() {
131+
this.transitionYearsName = 'slidenext'
132+
this.getYears(this.years[this.years.length - 1] + 1)
133+
this.transitionKey++
134+
}
107135
}
108136
}
109-
}
110137
</script>
111138

112139
<style lang="scss" scoped>
@@ -118,13 +145,50 @@ export default {
118145
left: 0;
119146
right: 0;
120147
color: #424242;
121-
padding: 10px;
148+
padding-bottom: 10px;
149+
overflow: hidden;
150+
122151
&.dark {
123152
color: white;
124153
background-color: #424242;
125154
}
126155
.month-button {
127156
text-transform: capitalize;
128157
}
158+
159+
.back-button {
160+
margin-left: auto;
161+
}
162+
163+
&__actions {
164+
display: flex;
165+
justify-content: space-between;
166+
align-items: center;
167+
padding: 0 10px;
168+
height: 56px;
169+
170+
.chang-years-section {
171+
display: flex;
172+
align-items: center;
173+
gap: 15px;
174+
}
175+
}
176+
177+
.years-month-wrapper {
178+
margin-top: 15px;
179+
padding: 0 10px;
180+
display: flex;
181+
flex-wrap: wrap;
182+
justify-content: space-between;
183+
row-gap: 10px;
184+
column-gap: 0;
185+
width: 100%;
186+
box-sizing: border-box;
187+
188+
.month-button,
189+
.year-button {
190+
width: 30%;
191+
}
192+
}
129193
}
130-
</style>
194+
</style>

src/VueCtkDateTimePicker/_subs/PickersContainer/_subs/TimePicker.vue

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
: column.type === 'hours' ? onScrollHours($event) : column.type === 'minutes' ? onScrollMinutes($event) : column.type === 'seconds' ? onScrollSeconds($event) : onScrollApms($event)
1717
"
1818
>
19+
<div class="arrow-btn top-0">
20+
<v-b-arrow-button type="up" :color=color @click="handlePrevClick(column.type)"/>
21+
</div>
1922
<div>
2023
<div
2124
class="before"
@@ -46,11 +49,15 @@
4649
:style="[columnPadding]"
4750
/>
4851
</div>
52+
<div class="arrow-btn bottom-0">
53+
<v-b-arrow-button type="down" @click="handleNextClick(column.type)"/>
54+
</div>
4955
</div>
5056
</div>
5157
</template>
5258
<script>
5359
import moment from 'moment'
60+
import VBArrowButton from "@/components/arrow_button"
5461
5562
const ArrayHourRange = (start, end, twoDigit, isAfternoon, disabledHours, isTwelveFormat) => {
5663
return Array(end - start + 1).fill().map((_, idx) => {
@@ -104,6 +111,9 @@ const debounce = (fn, time) => {
104111
105112
export default {
106113
name: 'TimePicker',
114+
components: {
115+
VBArrowButton,
116+
},
107117
props: {
108118
value: { type: String, default: null },
109119
format: { type: String, default: null },
@@ -334,9 +344,25 @@ export default {
334344
this.initPositionView()
335345
},
336346
methods: {
347+
handlePrevClick(type) {
348+
if(this[type.slice(0, -1)] !== 0) {
349+
this[type.slice(0, -1)]--;
350+
}
351+
this.emitValue();
352+
},
353+
handleNextClick(type) {
354+
if(type === 'hours' && this.hour < 23) {
355+
this.hour++;
356+
} else if(type === 'minutes' && this.minute < 59) {
357+
this.minute++;
358+
} else if(type === 'seconds' && this.second < 59) {
359+
this.second++;
360+
}
361+
this.emitValue();
362+
},
337363
getValue (scroll) {
338364
const itemHeight = 28
339-
const scrollTop = scroll.target.scrollTop
365+
const scrollTop = scroll.target.scrollTop - 18 // Remove arrow button height.
340366
return Math.round(scrollTop / itemHeight)
341367
},
342368
onScrollHours: debounce(function (scroll) {
@@ -501,6 +527,13 @@ export default {
501527
</script>
502528

503529
<style lang="scss" scoped>
530+
.arrow-btn {
531+
position: sticky;
532+
background-color:white;
533+
z-index: 1;
534+
text-align: center;
535+
}
536+
504537
.time-picker-column::-webkit-scrollbar {
505538
display: none;
506539
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.top-0 {
2+
top: 0;
3+
}
4+
5+
.bottom-0 {
6+
bottom: 0;
7+
}

src/assets/scss/helpers/index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
@import "./_animation.scss";
77
@import "./_typo.scss";
88
@import "./_width_height.scss";
9+
@import "./_position.scss";
910

1011
*, *::before, *::after {
1112
box-sizing: border-box;

0 commit comments

Comments
 (0)