Skip to content

Instantly share code, notes, and snippets.

@codenamezjames
Created June 29, 2020 16:26
Show Gist options
  • Select an option

  • Save codenamezjames/fea38f8b6bf68d69a7d2484a88e8c086 to your computer and use it in GitHub Desktop.

Select an option

Save codenamezjames/fea38f8b6bf68d69a7d2484a88e8c086 to your computer and use it in GitHub Desktop.

Revisions

  1. codenamezjames created this gist Jun 29, 2020.
    486 changes: 486 additions & 0 deletions chrono-chooser.vue
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,486 @@
    <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>