Files
chrono/src/components/ViewControls.vue
T
Sebastian df531908f3 refactor: code clean up
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-07-06 21:09:07 -04:00

111 lines
3.0 KiB
Vue

<script setup lang="ts" generic="S extends TimeSpanLabel">
import { computed } from 'vue';
import { spanToDays, type TimeSpanLabel } from '@/types/spans';
import { daysVisibleRange, shiftLocalDays, shiftLocalMonths } from '@/utils/date';
const props = defineProps<{
mode: 'days' | 'month';
currentDate: Date;
spans?: readonly S[];
selectedSpan?: S;
}>();
const emit = defineEmits<{
'update:current-date': [date: Date];
'update:span': [span: S];
}>();
const spanDays = computed(() => props.selectedSpan ? spanToDays(props.selectedSpan) : 1);
function previousPeriod() {
emit('update:current-date', props.mode === 'month'
? shiftLocalMonths(props.currentDate, -1)
: shiftLocalDays(props.currentDate, -spanDays.value));
}
function nextPeriod() {
emit('update:current-date', props.mode === 'month'
? shiftLocalMonths(props.currentDate, 1)
: shiftLocalDays(props.currentDate, spanDays.value));
}
function goToToday() {
emit('update:current-date', new Date());
}
const periodLabel = computed(() => {
if (props.mode === 'month') {
return props.currentDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
}
const range = daysVisibleRange(props.currentDate, spanDays.value);
const start = new Date(range.start);
const end = shiftLocalDays(new Date(range.end), -1);
const formatOptions: Intl.DateTimeFormatOptions = { month: 'short', day: 'numeric' };
if (spanDays.value === 1) {
return start.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
}
if (start.getMonth() === end.getMonth()) {
return `${start.toLocaleDateString('en-US', formatOptions)} - ${end.getDate()}, ${start.getFullYear()}`;
}
return `${start.toLocaleDateString('en-US', formatOptions)} - ${end.toLocaleDateString('en-US', formatOptions)}, ${end.getFullYear()}`;
});
</script>
<template>
<div class="view-controls">
<div class="view-navigation">
<v-btn size="x-small" variant="text" icon="mdi-chevron-left" @click="previousPeriod" />
<v-btn size="x-small" variant="tonal" @click="goToToday">Today</v-btn>
<v-btn size="x-small" variant="text" icon="mdi-chevron-right" @click="nextPeriod" />
<span class="current-period">{{ periodLabel }}</span>
</div>
<v-spacer />
<div v-if="spans?.length" class="view-selector">
<v-btn
v-for="span in spans"
:key="span"
size="x-small"
variant="text"
:color="selectedSpan === span ? 'primary' : undefined"
@click="emit('update:span', span)"
>
{{ span.toUpperCase() }}
</v-btn>
</div>
</div>
</template>
<style scoped>
.view-controls {
display: flex;
align-items: center;
padding: 4px 8px;
border-bottom: 1px solid rgb(var(--v-border-color));
background-color: rgb(var(--v-theme-surface));
flex-shrink: 0;
}
.view-navigation {
display: flex;
align-items: center;
gap: 4px;
}
.current-period {
font-size: 14px;
font-weight: 500;
margin-left: 8px;
white-space: nowrap;
}
.view-selector {
display: flex;
align-items: center;
gap: 2px;
}
</style>