Skip to content

Instantly share code, notes, and snippets.

@gregveres
Last active January 19, 2022 22:14
Show Gist options
  • Save gregveres/2e24931280749fc5dca828780bedf7dc to your computer and use it in GitHub Desktop.
Save gregveres/2e24931280749fc5dca828780bedf7dc to your computer and use it in GitHub Desktop.
<script lang="ts">
import { convertDayStringToDate, formatDate, monthDay } from '@helpers/DateFormatter';
import { defineComponent, ref } from '@vue/composition-api';
import { VHover } from 'vuetify/lib';
export default defineComponent({
name: 'VDateTimePicker',
components: { VHover },
props: {
date: {
type: String,
required: true
},
time: {
type: String,
required: true
}
},
setup(props, { emit }) {
const internalDate = ref(props.date);
const internalTime = ref(props.time);
const showTime = ref(false);
const datePicker = ref('DATE');
const formattedDate = ref<string>('');
const year = ref<number>(0);
const updateDate = (str: string) => {
const date = convertDayStringToDate(str);
formattedDate.value = formatDate(date, monthDay);
year.value = date.getFullYear();
};
updateDate(props.date);
const formattedTime = ref<string>('');
const pm = ref<boolean>(false);
const updateTime = (changePeriod: boolean, usePM: boolean) => {
const [, h, min] = internalTime.value.match(/^(\d+)[:](\d+)$/) || new Array(3);
let hour = parseInt(h, 10);
if (changePeriod) {
if (usePM && h < 12) hour += 12;
if (!usePM && h > 12) hour -= 12;
internalTime.value = `${hour}:${min}`;
}
pm.value = hour >= 12;
if (hour === 0) hour = 12;
formattedTime.value = `${hour > 12 ? hour - 12 : hour}:${min}`;
};
updateTime(false, false);
const sendUpdate = () => {
emit('update', internalDate.value, internalTime.value);
};
const dateChange = (newDate: string) => {
updateDate(newDate);
sendUpdate();
showTime.value = true;
};
const timeChange = (newTime: string) => {
console.log(newTime);
internalTime.value = newTime;
updateTime(false, false);
sendUpdate();
};
const selectYear = () => {
datePicker.value = 'YEAR';
showTime.value = false;
};
const selectDate = () => {
showTime.value = false;
};
const selectTime = () => {
showTime.value = true;
};
const switchToPM = () => {
updateTime(true, true);
sendUpdate();
};
const switchToAM = () => {
updateTime(true, false);
sendUpdate();
};
return {
internalDate,
internalTime,
formattedDate,
year,
updateDate,
formattedTime,
pm,
updateTime,
dateChange,
timeChange,
showTime,
datePicker,
selectYear,
selectDate,
selectTime,
switchToAM,
switchToPM
};
}
});
</script>
<template>
<div>
<div class="primary pa-3">
<VHover v-slot="{ hover }">
<span class="head_small primary pa-1" :class="{ 'darken-1': hover }" @click="selectYear">{{ year }}</span>
</VHover>
<div class="d-flex align-center">
<VHover v-slot="{ hover }">
<div class="head_title primary pa-1" :class="{ 'darken-1': hover }" @click="selectDate">{{
formattedDate
}}</div>
</VHover>
<v-spacer />
<VHover v-slot="{ hover }">
<div class="head_title primary pa-1" :class="{ 'darken-1': hover }" @click="selectTime">{{
formattedTime
}}</div>
</VHover>
<div>
<VHover v-slot="{ hover }">
<div class="head_small primary px-3" :class="{ dim: pm, 'darken-1': hover }" @click="switchToAM">AM</div>
</VHover>
<VHover v-slot="{ hover }">
<div class="head_small primary px-3" :class="{ dim: !pm, 'darken-1': hover }" @click="switchToPM">PM</div>
</VHover>
</div>
</div>
</div>
<div class="text-center">
<v-date-picker
v-show="!showTime"
v-model="internalDate"
:active-picker="datePicker"
no-title
show-adjacent-months
@change="dateChange"
/>
<v-time-picker v-show="showTime" v-model="internalTime" scrollable no-title format="ampm" @input="timeChange" />
</div>
</div>
</template>
<style lang="scss" scoped>
.head_title {
font-size: 2.5rem;
font-weight: 500;
overflow: hidden;
color: white;
cursor: pointer;
}
.head_small {
font-size: 0.8rem;
font-weight: 500;
color: white;
cursor: pointer;
&.dim {
opacity: 0.5;
}
}
</style>
import dayjs, { Dayjs } from 'dayjs';
export const monthDay = 'MMM DD'; // August 2021
export function formatDate(date: Date, formatStr = 'PP'): string {
return dayjs(date).format(formatStr);
}
export function convertDayStringToDate(day: string): Date {
const [, year, month, date] = day.trim().match(/^(\d+)[/-](\d+)[/-](\d+)$/) || new Array(4);
return new Date(year, month - 1, date);
}
@gregveres
Copy link
Author

This is a prototype combined date time picker using Vuetify 2.x. There is a feature request in Vuetify 3 for a combined date time picker, but I couldn't wait until then. After reading about the feature request, I decided to model my prototype after one of the samples in the feature request.

I debated back and forth about adding in the tabs that are in the sample I was mimicing. In the end, I decided to not go with explicit tabs given that you get the same functionality clicking on the date and time strings.

I would have created a codesandbox.io project to demo it, but I couldn't get typescript, veutify and the composition-api working together in the project. Mostly, I couldn't get vuetify 2 to show up with its styles. I have an animated gif showing the behaviour that I will try to get uploaded here.

@gregveres
Copy link
Author

I have added a PR to add active-picker as a prop for the v-time-picker. If this gets accepted, then I will update the gist to use the active-picker prop so that the user will be able to switch between minutes and hours on the time. Without this prop, the user is stuck on the minutes picker for the time once they have made their first selection of the hour.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment