193 lines
5.2 KiB
Vue
193 lines
5.2 KiB
Vue
<script setup lang="ts">
|
|
import type { Timestamp } from '@quasar/quasar-ui-qcalendar';
|
|
import {
|
|
addToDate,
|
|
createDayList,
|
|
createNativeLocaleFormatter,
|
|
getEndOfWeek,
|
|
getStartOfWeek,
|
|
parseTimestamp,
|
|
today,
|
|
} from '@quasar/quasar-ui-qcalendar';
|
|
import { ref, reactive, computed } from 'vue';
|
|
|
|
const selectedDate = defineModel<string>();
|
|
|
|
const weekdays = reactive([1, 2, 3, 4, 5, 6, 0]);
|
|
const locale = ref('en-CA');
|
|
const monthFormatter = monthFormatterFunc();
|
|
const dayFormatter = dayFormatterFunc();
|
|
const weekdayFormatter = weekdayFormatterFunc();
|
|
|
|
const today2 = computed(() => parseTimestamp(today()));
|
|
|
|
const parsedStart = computed(() =>
|
|
getStartOfWeek(
|
|
parseTimestamp(selectedDate.value || today()) as Timestamp,
|
|
weekdays,
|
|
today2.value as Timestamp
|
|
)
|
|
);
|
|
|
|
const parsedEnd = computed(() =>
|
|
getEndOfWeek(
|
|
parseTimestamp(selectedDate.value || today()) as Timestamp,
|
|
weekdays,
|
|
today2.value as Timestamp
|
|
)
|
|
);
|
|
|
|
const days = computed(() => {
|
|
if (parsedStart.value && parsedEnd.value) {
|
|
return createDayList(parsedStart.value, parsedEnd.value, today2.value as Timestamp, weekdays);
|
|
}
|
|
return [];
|
|
});
|
|
|
|
const dayStyle = computed(() => ({ width: 100 / weekdays.length + '%' }));
|
|
|
|
function onPrev() {
|
|
selectedDate.value = addToDate(parsedStart.value, { day: -7 }).date;
|
|
}
|
|
|
|
function onNext() {
|
|
selectedDate.value = addToDate(parsedStart.value, { day: 7 }).date;
|
|
}
|
|
|
|
function dayClass(day: Timestamp) {
|
|
return { 'date-button': true, 'selected-date-button': selectedDate.value === day.date };
|
|
}
|
|
|
|
function monthFormatterFunc() {
|
|
const longOptions: Intl.DateTimeFormatOptions = { timeZone: 'UTC', month: 'long' };
|
|
const shortOptions: Intl.DateTimeFormatOptions = { timeZone: 'UTC', month: 'short' };
|
|
return createNativeLocaleFormatter(locale.value, (_tms, short) => short ? shortOptions : longOptions);
|
|
}
|
|
|
|
function weekdayFormatterFunc() {
|
|
const longOptions: Intl.DateTimeFormatOptions = { timeZone: 'UTC', weekday: 'long' };
|
|
const shortOptions: Intl.DateTimeFormatOptions = { timeZone: 'UTC', weekday: 'short' };
|
|
return createNativeLocaleFormatter(locale.value, (_tms, short) => short ? shortOptions : longOptions);
|
|
}
|
|
|
|
function dayFormatterFunc() {
|
|
const longOptions: Intl.DateTimeFormatOptions = { timeZone: 'UTC', day: '2-digit' };
|
|
const shortOptions: Intl.DateTimeFormatOptions = { timeZone: 'UTC', day: 'numeric' };
|
|
return createNativeLocaleFormatter(locale.value, (_tms, short) => short ? shortOptions : longOptions);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="title-bar" style="display: flex">
|
|
<button tabindex="0" class="date-button direction-button direction-button__left" @click="onPrev">
|
|
<span class="q-calendar__focus-helper" tabindex="-1" />
|
|
</button>
|
|
<div class="dates-holder">
|
|
<div :key="parsedStart?.date" class="internal-dates-holder">
|
|
<div v-for="day in days" :key="day.date" :style="dayStyle">
|
|
<button tabindex="0" style="width: 100%" :class="dayClass(day)" @click="selectedDate = day.date">
|
|
<span class="q-calendar__focus-helper" tabindex="-1" />
|
|
<div style="width: 100%; font-size: 0.9em">{{ monthFormatter(day, true) }}</div>
|
|
<div style="width: 100%; font-size: 1.2em; font-weight: 700">{{ dayFormatter(day, false) }}</div>
|
|
<div style="width: 100%; font-size: 1em">{{ weekdayFormatter(day, true) }}</div>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button tabindex="0" class="date-button direction-button direction-button__right" @click="onNext">
|
|
<span class="q-calendar__focus-helper" tabindex="-1" />
|
|
</button>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="sass">
|
|
.title-bar
|
|
position: relative
|
|
width: 100%
|
|
height: 70px
|
|
background: white
|
|
display: flex
|
|
flex-direction: row
|
|
flex: 1 0 100%
|
|
justify-content: space-between
|
|
align-items: center
|
|
overflow: hidden
|
|
border-radius: 3px
|
|
user-select: none
|
|
margin: 2px 0px 2px
|
|
|
|
.dates-holder
|
|
position: relative
|
|
width: 100%
|
|
align-items: center
|
|
display: flex
|
|
justify-content: space-between
|
|
color: #fff
|
|
overflow: hidden
|
|
user-select: none
|
|
|
|
.internal-dates-holder
|
|
position: relative
|
|
width: 100%
|
|
display: inline-flex
|
|
flex: 1 1 100%
|
|
flex-direction: row
|
|
justify-content: space-between
|
|
overflow: hidden
|
|
user-select: none
|
|
|
|
.direction-button
|
|
background: white
|
|
color: $primary
|
|
width: 40px
|
|
max-width: 50px !important
|
|
|
|
.direction-button__left
|
|
&:before
|
|
content: '<'
|
|
display: inline-flex
|
|
flex-direction: column
|
|
justify-content: center
|
|
height: 100%
|
|
font-weight: 900
|
|
font-size: 3em
|
|
|
|
.direction-button__right
|
|
&:before
|
|
content: '>'
|
|
display: inline-flex
|
|
flex-direction: column
|
|
justify-content: center
|
|
height: 100%
|
|
font-weight: 900
|
|
font-size: 3em
|
|
|
|
.date-button
|
|
color: $primary
|
|
background: white
|
|
z-index: 2
|
|
height: 100%
|
|
outline: 0
|
|
cursor: pointer
|
|
border-radius: 3px
|
|
display: inline-flex
|
|
flex: 1 0 auto
|
|
flex-direction: column
|
|
align-items: stretch
|
|
position: relative
|
|
border: 0
|
|
vertical-align: middle
|
|
padding: 0
|
|
font-size: 14px
|
|
line-height: 1.715em
|
|
text-decoration: none
|
|
font-weight: 500
|
|
text-transform: uppercase
|
|
text-align: center
|
|
user-select: none
|
|
|
|
.selected-date-button
|
|
color: white !important
|
|
background: $primary !important
|
|
</style>
|