overhauled calendar header
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user