Added arrow navigation on calendar page

This commit is contained in:
Alexis
2025-08-24 15:02:42 +02:00
parent 8dff0763e9
commit 8c53616c79
4 changed files with 158 additions and 104 deletions

View File

@@ -8,8 +8,8 @@ import CenturyLayout from "./state/centennially/Layout.vue"
import DecadeLayout from "./state/decennially/Layout.vue" import DecadeLayout from "./state/decennially/Layout.vue"
import YearLayout from "./state/yearly/Layout.vue" import YearLayout from "./state/yearly/Layout.vue"
const { currentConfig, jumpToDate, selectedDate } = useCalendar() const { currentConfig, jumpToDate, selectedDate, toPastFar, toPastNear, toFutureNear, toFutureFar } = useCalendar()
const { isReadOnly } = storeToRefs(useCalendar()) const { isReadOnly, calendarState } = storeToRefs(useCalendar())
const currentViewComponent: ComputedRef<Component> = computed<Component>(() => { const currentViewComponent: ComputedRef<Component> = computed<Component>(() => {
switch (currentConfig.viewType) { switch (currentConfig.viewType) {
@@ -31,6 +31,34 @@ const currentViewComponent: ComputedRef<Component> = computed<Component>(() => {
onMounted(() => { onMounted(() => {
jumpToDate(selectedDate) jumpToDate(selectedDate)
}) })
// Key combos to navigate
const { arrowUp, arrowLeft, pageUp } = useMagicKeys()
const { arrowDown, arrowRight, pageDown } = useMagicKeys()
watch([arrowUp, arrowLeft], (k) => {
if (calendarState.value === 'active' && k[0] || k[1]) {
toPastNear()
}
})
watch([arrowDown, arrowRight], (k) => {
if (calendarState.value === 'active' && k[0] || k[1]) {
toFutureNear()
}
})
watch(pageUp!, (k) => {
if (calendarState.value === 'active' && k) {
toPastFar()
}
})
watch(pageDown!, (k) => {
if (calendarState.value === 'active' && k) {
toFutureFar()
}
})
</script> </script>
<template> <template>

View File

@@ -3,20 +3,8 @@ import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
import { PhPlus } from "@phosphor-icons/vue"; import { PhPlus } from "@phosphor-icons/vue";
import { VisuallyHidden } from "radix-vue"; import { VisuallyHidden } from "radix-vue";
const isDialogOpen = ref<boolean>(false); const { isCreatingEventModalOpen } = storeToRefs(useCalendar());
const { resetSkeleton } = useCalendar(); const { resetSkeleton, toggleCreatingEventModal } = useCalendar();
// Watch the popover state
watch(isDialogOpen, (hasOpened, _o) => {
if (hasOpened) {
resetSkeleton()
}
})
// Toggles the dialog
function toggleDialog() {
isDialogOpen.value = !isDialogOpen.value;
};
/** /**
* Prevents the modal from closing if's still loading * Prevents the modal from closing if's still loading
@@ -36,7 +24,7 @@ const breakpoints = useBreakpoints(
<UiButton <UiButton
class="max-md:fixed max-md:bottom-8 max-md:right-8 max-md:z-50 max-md:size-14 max-md:rounded-xl" class="max-md:fixed max-md:bottom-8 max-md:right-8 max-md:z-50 max-md:size-14 max-md:rounded-xl"
:size="breakpoints.md.value ? 'default' : 'icon'" :size="breakpoints.md.value ? 'default' : 'icon'"
@click="toggleDialog" @click="toggleCreatingEventModal"
> >
<PhPlus :size="breakpoints.md.value ? 18 : 24" weight="bold" /> <PhPlus :size="breakpoints.md.value ? 18 : 24" weight="bold" />
@@ -47,7 +35,7 @@ const breakpoints = useBreakpoints(
</Transition> </Transition>
</ClientOnly> </ClientOnly>
<UiDialog v-model:open="isDialogOpen"> <UiDialog v-model:open="isCreatingEventModalOpen">
<UiDialogContent <UiDialogContent
class="max-md:translate-0 max-md:inset-0 max-md:w-full max-md:block" class="max-md:translate-0 max-md:inset-0 max-md:w-full max-md:block"
:trap-focus="true" :trap-focus="true"
@@ -64,7 +52,7 @@ const breakpoints = useBreakpoints(
</UiDialogDescription> </UiDialogDescription>
</VisuallyHidden> </VisuallyHidden>
<CalendarFormCreateEvent @event-created="toggleDialog" /> <CalendarFormCreateEvent @event-created="toggleCreatingEventModal" />
</UiDialogContent> </UiDialogContent>
</UiDialog> </UiDialog>
</template> </template>

View File

@@ -12,7 +12,7 @@ interface DirectionLabels {
futureFar: string futureFar: string
} }
const { currentConfig, decrementViewMonth, incrementViewMonth, decrementViewYear, incrementViewYear } = const { currentConfig, toPastFar, toPastNear, toFutureNear, toFutureFar } =
useCalendar() useCalendar()
const activeDirectionLabels: ComputedRef<DirectionLabels> = computed(() => { const activeDirectionLabels: ComputedRef<DirectionLabels> = computed(() => {
@@ -51,90 +51,6 @@ const activeDirectionLabels: ComputedRef<DirectionLabels> = computed(() => {
} }
} }
}) })
function toPastFar(): void {
switch (currentConfig.viewType) {
case "month":
decrementViewYear()
break
case "year":
decrementViewYear(10)
break
case "decade":
decrementViewYear(100)
break
case "century":
default:
decrementViewYear(1000)
break
}
}
function toPastNear(): void {
switch (currentConfig.viewType) {
case "month":
decrementViewMonth()
break
case "year":
decrementViewYear()
break
case "decade":
decrementViewYear(10)
break
case "century":
default:
decrementViewYear(100)
break
}
}
function toFutureNear(): void {
switch (currentConfig.viewType) {
case "month":
incrementViewMonth()
break
case "year":
incrementViewYear()
break
case "decade":
incrementViewYear(10)
break
case "century":
default:
incrementViewYear(100)
break
}
}
function toFutureFar(): void {
switch (currentConfig.viewType) {
case "month":
incrementViewYear()
break
case "year":
incrementViewYear(10)
break
case "decade":
incrementViewYear(100)
break
case "century":
default:
incrementViewYear(1000)
break
}
}
</script> </script>
<template> <template>

View File

@@ -10,6 +10,8 @@ import type { CalendarEvent } from "@@/models/CalendarEvent"
import type { CalendarMonth } from "@@/models/CalendarMonth" import type { CalendarMonth } from "@@/models/CalendarMonth"
import type { Category } from "@@/models/Category" import type { Category } from "@@/models/Category"
type CalendarState = "active" | "idle"
type CalendarViewType = "month" | "year" | "decade" | "century" type CalendarViewType = "month" | "year" | "decade" | "century"
type CalendarCurrentConfig = { type CalendarCurrentConfig = {
@@ -668,6 +670,90 @@ export const useCalendar = defineStore("calendar", () => {
currentEvents.value = computeCurrentEvents() currentEvents.value = computeCurrentEvents()
}, { deep: true, immediate: true }) }, { deep: true, immediate: true })
function toPastFar(): void {
switch (currentConfig.value.viewType) {
case "month":
decrementViewYear()
break
case "year":
decrementViewYear(10)
break
case "decade":
decrementViewYear(100)
break
case "century":
default:
decrementViewYear(1000)
break
}
}
function toPastNear(): void {
switch (currentConfig.value.viewType) {
case "month":
decrementViewMonth()
break
case "year":
decrementViewYear()
break
case "decade":
decrementViewYear(10)
break
case "century":
default:
decrementViewYear(100)
break
}
}
function toFutureNear(): void {
switch (currentConfig.value.viewType) {
case "month":
incrementViewMonth()
break
case "year":
incrementViewYear()
break
case "decade":
incrementViewYear(10)
break
case "century":
default:
incrementViewYear(100)
break
}
}
function toFutureFar(): void {
switch (currentConfig.value.viewType) {
case "month":
incrementViewYear()
break
case "year":
incrementViewYear(10)
break
case "decade":
incrementViewYear(100)
break
case "century":
default:
incrementViewYear(1000)
break
}
}
/** /**
* Determines if the event can appear in the front end * Determines if the event can appear in the front end
* *
@@ -805,6 +891,22 @@ export const useCalendar = defineStore("calendar", () => {
} }
} }
/**
* State for event modal creation
*/
const isCreatingEventModalOpen = ref<boolean>(false)
// Watch the popover state
watch(isCreatingEventModalOpen, (hasOpened, _o) => {
if (hasOpened) {
resetSkeleton()
}
})
function toggleCreatingEventModal() {
isCreatingEventModalOpen.value = !isCreatingEventModalOpen.value
}
/** /**
* State for event modal edition * State for event modal edition
*/ */
@@ -936,6 +1038,19 @@ export const useCalendar = defineStore("calendar", () => {
isCategoriesModalOpen.value = state isCategoriesModalOpen.value = state
} }
/**
* Handle the state of the active calendar for UI interactions
*/
const calendarState = ref<CalendarState>("active")
watch([isCategoriesModalOpen, isCreatingEventModalOpen, isDeleteEventModalOpen, isEditEventModalOpen, isAdvancedSearchOpen, isDraggingEvent], (values) => {
if (values.every(v => !v)) {
calendarState.value = 'active'
} else {
calendarState.value = 'idle'
}
})
return { return {
isReadOnly, isReadOnly,
setReadStatus, setReadStatus,
@@ -975,10 +1090,15 @@ export const useCalendar = defineStore("calendar", () => {
areDatesIdentical, areDatesIdentical,
compareDates, compareDates,
getRelativeString, getRelativeString,
toPastFar,
toPastNear,
toFutureNear,
toFutureFar,
baseEvents, baseEvents,
allEvents, allEvents,
categories, categories,
currentEvents, currentEvents,
calendarState,
getRelativeEventFromDate, getRelativeEventFromDate,
getRelativeEventFromEvent, getRelativeEventFromEvent,
cancelLatestRequest, cancelLatestRequest,
@@ -993,6 +1113,8 @@ export const useCalendar = defineStore("calendar", () => {
lastActiveEvent, lastActiveEvent,
updateEventFromSkeleton, updateEventFromSkeleton,
deleteEventFromSkeleton, deleteEventFromSkeleton,
isCreatingEventModalOpen,
toggleCreatingEventModal,
isEditEventModalOpen, isEditEventModalOpen,
revealEditEventModal, revealEditEventModal,
isDeleteEventModalOpen, isDeleteEventModalOpen,