Created
June 29, 2020 16:26
-
-
Save codenamezjames/fea38f8b6bf68d69a7d2484a88e8c086 to your computer and use it in GitHub Desktop.
quasar date range picker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div> | |
<div class="row q-col-gutter-sm q-mb-md q-px-md q-pt-md"> | |
<div class="col flex items-center q-gutter-sm"> | |
<q-input | |
v-model="zFromUs" | |
label="From" | |
outlined | |
dense | |
mask="##-##-####" | |
style="width: 140px;" | |
> | |
<template #prepend> | |
<q-icon | |
:name="mdiCalendar" | |
color="primary" | |
/> | |
</template> | |
</q-input> | |
<q-btn | |
outline | |
color="transition-2" | |
:label="zStart" | |
> | |
<q-popup-proxy | |
transition-show="scale" | |
transition-hide="scale" | |
@before-show="zStartPicker = zStart" | |
> | |
<q-time | |
v-model="zStartPicker" | |
mask="hh:mm A" | |
> | |
<div class="row items-center justify-end q-gutter-sm"> | |
<div class="col"> | |
<q-btn | |
:icon="mdiClockTimeTwelveOutline" | |
flat | |
round | |
@click="zStartPicker = '12:00 AM'" | |
> | |
<q-tooltip>Set time to start of day</q-tooltip> | |
</q-btn> | |
</div> | |
<q-btn | |
v-close-popup | |
label="Cancel" | |
color="primary" | |
flat | |
/> | |
<q-btn | |
v-close-popup | |
label="OK" | |
color="primary" | |
flat | |
@click="saveTime('zStart')" | |
/> | |
</div> | |
</q-time> | |
</q-popup-proxy> | |
</q-btn> | |
<div><hr style="width: 10px"></div> | |
<!-- TODO: from and to should only be from past to future --> | |
<q-input | |
v-model="zToUs" | |
label="To" | |
outlined | |
dense | |
mask="##-##-####" | |
style="width: 140px;" | |
> | |
<template #prepend> | |
<q-icon | |
:name="mdiCalendar" | |
color="primary" | |
/> | |
</template> | |
</q-input> | |
<q-btn | |
outline | |
color="transition-2" | |
:label="zEnd" | |
> | |
<q-popup-proxy | |
transition-show="scale" | |
transition-hide="scale" | |
@before-show="zEndPicker = zEnd" | |
> | |
<q-time | |
v-model="zEndPicker" | |
mask="hh:mm A" | |
> | |
<div class="row items-center justify-end q-gutter-sm"> | |
<div class="col"> | |
<q-btn | |
:icon="mdiClockTimeElevenOutline" | |
flat | |
round | |
@click="zEndPicker = '11:59 PM'" | |
> | |
<q-tooltip>Set time to end of day</q-tooltip> | |
</q-btn> | |
</div> | |
<q-btn | |
v-close-popup | |
label="Cancel" | |
color="primary" | |
flat | |
/> | |
<q-btn | |
v-close-popup | |
label="OK" | |
color="primary" | |
flat | |
@click="saveTime('zEnd')" | |
/> | |
</div> | |
</q-time> | |
</q-popup-proxy> | |
</q-btn> | |
</div> | |
</div> | |
<q-separator /> | |
<div class="row q-col-gutter-lg q-px-md"> | |
<div class="col-auto"> | |
<q-list | |
dense | |
class="q-my-md" | |
> | |
<q-item | |
clickable | |
:active="activeItemSelection === 'today'" | |
@click="setRange('today')" | |
> | |
<q-item-section>Today</q-item-section> | |
</q-item> | |
<q-item | |
clickable | |
:active="activeItemSelection === 'yesterday'" | |
@click="setRange('yesterday')" | |
> | |
<q-item-section>Yesterday</q-item-section> | |
</q-item> | |
<q-item | |
clickable | |
:active="activeItemSelection === 'thisWeek'" | |
@click="setRange('thisWeek')" | |
> | |
<q-item-section>This Week</q-item-section> | |
</q-item> | |
<q-item | |
clickable | |
:active="activeItemSelection === 'lastWeek'" | |
@click="setRange('lastWeek')" | |
> | |
<q-item-section>Last Week</q-item-section> | |
</q-item> | |
<q-item | |
clickable | |
:active="activeItemSelection === 'thisMonth'" | |
@click="setRange('thisMonth')" | |
> | |
<q-item-section>This Month</q-item-section> | |
</q-item> | |
<q-item | |
clickable | |
:active="activeItemSelection === 'lastMonth'" | |
@click="setRange('lastMonth')" | |
> | |
<q-item-section>Last Month</q-item-section> | |
</q-item> | |
<q-item | |
clickable | |
:active="activeItemSelection === 'thisYear'" | |
@click="setRange('thisYear')" | |
> | |
<q-item-section>This Year</q-item-section> | |
</q-item> | |
<q-item | |
clickable | |
:active="activeItemSelection === 'lastYear'" | |
@click="setRange('lastYear')" | |
> | |
<q-item-section>Last Year</q-item-section> | |
</q-item> | |
</q-list> | |
</div> | |
<div class="col-auto"> | |
<q-separator | |
vertical | |
style="height: 100%" | |
/> | |
</div> | |
<div class="col column"> | |
<div class="row q-col-gutter-md col q-pt-md q-mb-md"> | |
<div class="col"> | |
<month-year-chooser | |
:year.sync="cal1Year" | |
:month.sync="cal1Month" | |
class="q-mb-sm" | |
/> | |
<q-calendar | |
v-model="cal1" | |
view="month" | |
locale="en-us" | |
mini-mode | |
no-active-date | |
short-weekday-label | |
animated | |
:selected-start-end-dates="startEndDates" | |
:day-class="classDay" | |
@click:date="onClickDay" | |
/> | |
</div> | |
<div class="col-auto"> | |
<q-separator | |
vertical | |
style="height: 100%" | |
/> | |
</div> | |
<div class="col"> | |
<month-year-chooser | |
:year.sync="cal2Year" | |
:month.sync="cal2Month" | |
class="q-mb-sm" | |
/> | |
<q-calendar | |
v-model="cal2" | |
view="month" | |
locale="en-us" | |
mini-mode | |
no-active-date | |
short-weekday-label | |
animated | |
:selected-start-end-dates="startEndDates" | |
:day-class="classDay" | |
@click:date="onClickDay" | |
/> | |
</div> | |
</div> | |
<q-separator | |
class="q-mb-md" | |
style="margin-left: -24px; margin-right: -24px; width: calc(100% + 40px);" | |
/> | |
<div class="row items-center q-mb-md q-col-gutter-md"> | |
<div class="col-auto"> | |
Range: {{ range }} | |
</div> | |
<div class="col"> | |
<slot /> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</template> | |
<script> | |
import { mdiClose, mdiCalendar, mdiClockTimeTwelveOutline, mdiClockTimeElevenOutline } from '@quasar/extras/mdi-v5' | |
import dayjs from 'dayjs' | |
import { autoPluralize } from 'src/tools' | |
import isEqual from 'lodash/isEqual' | |
// import QCalendar from '@quasar/quasar-ui-qcalendar' | |
const todayFrom = dayjs() | |
const todayTo = dayjs() | |
export default { | |
components: { | |
monthYearChooser: () => import('src/modules/month-year-chooser/index.vue') | |
}, | |
constants: { | |
mdiClose, | |
mdiCalendar, | |
mdiClockTimeTwelveOutline, | |
mdiClockTimeElevenOutline | |
}, | |
props: { | |
value: { | |
default: () => [ | |
dayjs().toISOString(), | |
dayjs().toISOString() | |
], | |
validator (input) { | |
if (input) return Array.isArray(input) && input.length === 2 | |
return true | |
} | |
} | |
}, | |
data () { | |
return { | |
zStartPicker: '00:00', | |
zEndPicker: '00:00', | |
zFrom: dayjs(this.value[0]).format('YYYY-MM-DD'), | |
zTo: dayjs(this.value[1]).format('YYYY-MM-DD'), | |
zStart: dayjs(this.value[0]).format('hh:mm A'), | |
zEnd: dayjs(this.value[1]).format('hh:mm A'), | |
clicked: false, | |
cal1: dayjs().subtract(1, 'month').format('YYYY-MM-DD'), | |
cal2: dayjs().format('YYYY-MM-DD'), | |
timeFrames: { | |
today: { from: todayFrom, to: todayTo }, | |
yesterday: { from: todayFrom.add(-1, 'day'), to: todayTo.add(-1, 'day') }, | |
thisWeek: { from: todayFrom.startOf('w'), to: todayTo }, | |
lastWeek: { from: todayFrom.add(-1, 'w').startOf('w'), to: todayTo.add(-1, 'w').endOf('w') }, | |
thisMonth: { from: todayFrom.startOf('M'), to: todayTo }, | |
lastMonth: { from: todayFrom.add(-1, 'M').startOf('M'), to: todayTo.add(-1, 'M').endOf('M') }, | |
thisYear: { from: todayFrom.startOf('y'), to: todayTo }, | |
lastYear: { from: todayFrom.add(-1, 'y').startOf('y'), to: todayTo.add(-1, 'y').endOf('y') } | |
} | |
} | |
}, | |
computed: { | |
activeItemSelection () { | |
const from = dayjs(this.zFrom, 'YYYY-MM-DD') | |
const to = dayjs(this.zTo, 'YYYY-MM-DD') | |
let active = '' | |
;['lastYear', 'thisYear', 'lastMonth', 'thisMonth', 'lastWeek', 'thisWeek', 'yesterday', 'today'].forEach((name) => { | |
if (to.isSame(this.timeFrames[name].to, 'date') && from.isSame(this.timeFrames[name].from, 'date')) active = name | |
}) | |
return active | |
}, | |
range () { | |
const days = Math.abs(dayjs(this.zFrom, 'YYYY-MM-DD').diff(this.zTo, 'day')) + 1 | |
return days + autoPluralize(days, ' Days') | |
}, | |
cal1Month: { | |
get () { | |
return (+dayjs(this.cal1, 'YYYY-MM-DD').format('MM')) - 1 | |
}, | |
set (month) { | |
const newDate = dayjs(this.cal1).month(month) | |
if (newDate.isValid()) this.cal1 = newDate.format('YYYY-MM-DD') | |
} | |
}, | |
cal1Year: { | |
get () { | |
return dayjs(this.cal1, 'YYYY-MM-DD').format('YYYY') | |
}, | |
set (year) { | |
const newDate = dayjs(this.cal1).year(year) | |
if (newDate.isValid()) this.cal1 = newDate.format('YYYY-MM-DD') | |
} | |
}, | |
cal2Month: { | |
get () { | |
return (+dayjs(this.cal2, 'YYYY-MM-DD').format('MM')) - 1 | |
}, | |
set (month) { | |
const newDate = dayjs(this.cal2, 'YYYY-MM-DD').month(month) | |
if (newDate.isValid()) this.cal2 = newDate.format('YYYY-MM-DD') | |
} | |
}, | |
cal2Year: { | |
get () { | |
return dayjs(this.cal2, 'YYYY-MM-DD').format('YYYY') | |
}, | |
set (year) { | |
const newDate = dayjs(this.cal2, 'YYYY-MM-DD').year(year) | |
if (newDate.isValid()) this.cal2 = newDate.format('YYYY-MM-DD') | |
} | |
}, | |
zFromUs: { | |
get () { return dayjs(this.zFrom, 'YYYY-MM-DD').format('MM-DD-YYYY') }, | |
set (date) { | |
if (dayjs(date, 'MM-DD-YYYY').isValid()) this.zFrom = dayjs(date).format('YYYY-MM-DD') | |
this.cal1 = this.zFrom | |
if (dayjs(this.zFrom, 'YYYY-MM-DD').isSame(dayjs(this.zTo, 'YYYY-MM-DD'), 'M')) { | |
this.cal1 = dayjs(this.zFrom, 'YYYY-MM-DD').add(-1, 'M').format('YYYY-MM-DD') | |
} | |
} | |
}, | |
zToUs: { | |
get () { return dayjs(this.zTo, 'YYYY-MM-DD').format('MM-DD-YYYY') }, | |
set (date) { | |
if (dayjs(date, 'MM-DD-YYYY').isValid()) this.zTo = dayjs(date).format('YYYY-MM-DD') | |
this.cal2 = this.zTo | |
if (dayjs(this.zFrom, 'YYYY-MM-DD').isSame(dayjs(this.zTo, 'YYYY-MM-DD'), 'M')) { | |
this.cal1 = dayjs(this.zFrom, 'YYYY-MM-DD').add(-1, 'M').format('YYYY-MM-DD') | |
} | |
} | |
}, | |
startEndDates () { | |
const dates = [] | |
if (this.zFromUnix !== false && this.zToUnix !== false) { | |
if (this.zFromUnix <= this.zToUnix) { | |
dates.push(this.zFrom, this.zTo) | |
} else { | |
dates.push(this.zTo, this.zFrom) | |
} | |
} | |
return dates | |
}, | |
zFromUnix () { | |
if (this.zFrom !== '') { | |
return dayjs(this.zFrom, 'YYYY-MM-DD').unix() | |
} | |
return false | |
}, | |
zToUnix () { | |
if (this.zTo !== '') { | |
return dayjs(this.zTo, 'YYYY-MM-DD').unix() | |
} | |
return false | |
} | |
}, | |
watch: { | |
startEndDates () { this.runUpdate() }, | |
zStart () { this.runUpdate() }, | |
zEnd () { this.runUpdate() }, | |
value () { | |
if (isEqual(this.value.map(d => dayjs(d).format('YYYY-MM-DD')), this.startEndDates)) return | |
this.zFrom = dayjs(this.value[0]).format('YYYY-MM-DD') | |
this.zTo = dayjs(this.value[1]).format('YYYY-MM-DD') | |
} | |
}, | |
created () { | |
this.cal1 = this.zFrom | |
this.cal2 = this.zTo | |
if (dayjs(this.zFrom, 'YYYY-MM-DD').isSame(dayjs(this.zTo, 'YYYY-MM-DD'), 'M')) { | |
this.cal1 = dayjs(this.zFrom, 'YYYY-MM-DD').add(-1, 'M').format('YYYY-MM-DD') | |
} | |
}, | |
methods: { | |
runUpdate () { | |
const startEndISO = [ | |
dayjs(`${this.startEndDates[0]} ${this.zStart}`, 'YYYY-MM-DD hh:mm A').toISOString(), | |
dayjs(`${this.startEndDates[1]} ${this.zEnd}`, 'YYYY-MM-DD hh:mm A').toISOString() | |
] | |
if (isEqual(this.value, startEndISO)) return | |
this.$emit('input', startEndISO) | |
}, | |
saveTime (model) { | |
this[model] = this[`${model}Picker`] | |
}, | |
classDay (timestamp) { | |
if (this.zFromUnix !== false && this.zToUnix !== false) { | |
return this.getBetween(timestamp) | |
} | |
}, | |
getBetween ({ date }) { | |
const nowIdentifier = dayjs(date, 'YYYY-MM-DD').unix() | |
const lower = Math.min(this.zFromUnix, this.zToUnix) | |
const higher = Math.max(this.zFromUnix, this.zToUnix) | |
return { | |
'q-selected-day-first': lower === nowIdentifier, | |
'q-selected-day': lower <= nowIdentifier && higher >= nowIdentifier, | |
'q-selected-day-last': higher === nowIdentifier | |
} | |
}, | |
onClickDay ({ date }) { | |
if (this.clicked === false) { | |
this.zFrom = date | |
this.zTo = date | |
this.clicked = true | |
return | |
} | |
// mouse is down, start selection and capture current | |
this.clicked = false | |
this.zTo = date | |
}, | |
setRange (when) { | |
const doWhen = this.timeFrames[when] | |
this.zFrom = doWhen.from.format('YYYY-MM-DD') | |
this.zTo = doWhen.to.format('YYYY-MM-DD') | |
this.cal1 = this.zFrom | |
this.cal2 = this.zTo | |
if (dayjs(this.zFrom, 'YYYY-MM-DD').isSame(dayjs(this.zTo, 'YYYY-MM-DD'), 'M')) { | |
this.cal1 = dayjs(this.zFrom, 'YYYY-MM-DD').add(-1, 'M').format('YYYY-MM-DD') | |
} | |
} | |
} | |
} | |
</script> | |
<style lang="scss" scoped></style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment