overhauled calendar header

This commit is contained in:
2025-10-06 23:53:49 -04:00
parent c883444de6
commit e158427c93

View File

@@ -1,10 +1,39 @@
<script setup lang="ts">
import { ref, watch, nextTick, computed } from 'vue'
import { ref, watch, nextTick, computed, onMounted } from 'vue'
import FullCalendar from '@fullcalendar/vue3'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'
import { X, Clock, MapPin, User, ListTodo } from 'lucide-vue-next'
import { X, Clock, MapPin, User, ListTodo, ChevronLeft, ChevronRight, Plus } from 'lucide-vue-next'
const monthLabels = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
]
const selectedMonth = ref<number>(new Date().getMonth())
const selectedYear = ref<number>(new Date().getFullYear())
const years = Array.from({ length: 41 }, (_, i) => selectedYear.value - 20 + i) // +/- 20 yrs
function api() {
return calendarRef.value?.getApi()
}
// keep dropdowns in sync whenever the calendar navigates
function onDatesSet() {
const d = api()?.getDate() ?? new Date()
selectedMonth.value = d.getMonth()
selectedYear.value = d.getFullYear()
}
function goPrev() { api()?.prev() }
function goNext() { api()?.next() }
function goToday() { api()?.today() }
// jump to the selected month/year
function goToSelectedDate() {
api()?.gotoDate(new Date(selectedYear.value, selectedMonth.value, 1))
}
type CalEvent = {
id: string
@@ -52,8 +81,8 @@ const calendarOptions = ref({
height: '100%',
expandRows: true,
headerToolbar: {
left: 'prev,next today',
center: 'title',
left: '',
center: '',
right: ''
},
events,
@@ -98,6 +127,8 @@ const calendarOptions = ref({
},
})
watch(panelOpen, async () => {
await nextTick()
calendarRef.value?.getApi().updateSize()
@@ -121,6 +152,15 @@ const whenText = computed(() => {
: `${startFmt.format(s)}`
})
function onCreateEvent() {
const iso = new Date().toISOString().slice(0, 10) // YYYY-MM-DD
onDateClick({ dateStr: iso })
}
onMounted(() => {
onDatesSet()
})
const ext = computed(() => activeEvent.value?.extendedProps ?? {})
</script>
@@ -128,6 +168,55 @@ const ext = computed(() => activeEvent.value?.extendedProps ?? {})
<div class="flex">
<div class="flex-1 min-h-0 mt-5">
<div class="h-[80vh] min-h-0">
<!-- calendar header -->
<div class="flex items-center justify-between mx-5">
<!-- Left: title + pickers -->
<div class="flex items-center gap-2">
<!-- <h2 class="text-xl font-semibold tracking-tight">
{{ monthLabels[selectedMonth] }} {{ selectedYear }}
</h2> -->
<!-- Month dropdown -->
<select v-model.number="selectedMonth" @change="goToSelectedDate"
class="ml-2 rounded-md border bg-transparent px-2 py-1 text-sm" aria-label="Select month">
<option v-for="(m, i) in monthLabels" :key="m" :value="i" class="bg-card">
{{ m }}
</option>
</select>
<!-- Year dropdown -->
<select v-model.number="selectedYear" @change="goToSelectedDate"
class="rounded-md border bg-transparent px-2 py-1 text-sm" aria-label="Select year">
<option v-for="y in years" :key="y" :value="y" class="bg-card">
{{ y }}
</option>
</select>
</div>
<!-- Right: nav + today + create -->
<div class="flex items-center gap-2">
<button
class="inline-flex h-8 w-8 items-center justify-center rounded-md border hover:bg-muted/40"
aria-label="Previous month" @click="goPrev">
<ChevronLeft class="h-4 w-4" />
</button>
<button
class="inline-flex h-8 w-8 items-center justify-center rounded-md border hover:bg-muted/40"
aria-label="Next month" @click="goNext">
<ChevronRight class="h-4 w-4" />
</button>
<button class="ml-1 rounded-md border px-3 py-1.5 text-sm hover:bg-muted/40" @click="goToday">
Today
</button>
<button
class="ml-1 inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground hover:opacity-90"
@click="onCreateEvent">
<Plus class="h-4 w-4" />
Create
</button>
</div>
</div>
<FullCalendar ref="calendarRef" :options="calendarOptions" />
</div>
</div>