8 Commits

Author SHA1 Message Date
Alexis
7443769f00 Updated package 2025-10-06 21:37:31 +02:00
Alexis
692aaad868 Updated package 2025-09-12 19:52:32 +02:00
Alexis
371bb7e38a Updated package 2025-09-03 21:14:07 +02:00
Alexis
c4e70eb29c Changed quick navigation (arrows will be for days) 2025-08-24 19:53:43 +02:00
Alexis
f8d2387b59 Added quick navigation for calendar pages 2025-08-24 19:44:27 +02:00
Alexis
8c53616c79 Added arrow navigation on calendar page 2025-08-24 15:02:42 +02:00
Alexis
8dff0763e9 Changed calendar menu subnav 2025-08-24 13:47:03 +02:00
Alexis
9c7b503ca8 Merge branch 'feat/nav' into dev 2025-08-24 11:49:21 +02:00
9 changed files with 2863 additions and 3554 deletions

View File

@@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useCalendar } from "@/stores/CalendarStore" import { useCalendar } from "@/stores/CalendarStore"
import { computed, type Component, type ComputedRef } from "vue"
// import { PhMagnifyingGlass } from '@phosphor-icons/vue' // import { PhMagnifyingGlass } from '@phosphor-icons/vue'
import MonthlyLayout from "./state/monthly/Layout.vue" import MonthlyLayout from "./state/monthly/Layout.vue"
@@ -8,8 +7,10 @@ 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() import { onKeyDown } from "@vueuse/core"
const { isReadOnly } = storeToRefs(useCalendar())
const { currentConfig, jumpToDate, selectedDate, toPastFar, toPastNear, toFutureNear, toFutureFar, toggleCreatingEventModal } = 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 +32,45 @@ const currentViewComponent: ComputedRef<Component> = computed<Component>(() => {
onMounted(() => { onMounted(() => {
jumpToDate(selectedDate) jumpToDate(selectedDate)
}) })
/**
* Keyboard quick controls
*/
// Key combos to navigate
const {
home, pageUp,
end, pageDown,
} = useMagicKeys()
watch(home!, (k) => {
if (calendarState.value === 'active' && k) {
toPastFar()
}
})
watch(pageUp!, (k) => {
if (calendarState.value === 'active' && k) {
toPastNear()
}
})
watch(pageDown!, (k) => {
if (calendarState.value === 'active' && k) {
toFutureNear()
}
})
watch(end!, (k) => {
if (calendarState.value === 'active' && k) {
toFutureFar()
}
})
// Key combo to create an event
onKeyDown("+", (e) => {
if (calendarState.value === 'active') {
e.preventDefault()
toggleCreatingEventModal()
}
})
</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,101 +51,17 @@ 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>
<div class="flex gap-2"> <div class="flex gap-1">
<div class="grid items-center w-40 px-3 md:px-4 py-2 bg-background border-border border-x-[1px] border-t-[1px] rounded-t-sm text-sm max-md:text-xs transition-colors"> <div class="grid items-center w-40 px-1.5 md:px-3 py-1 bg-background border-border border-x-[1px] border-t-[1px] rounded-t-sm">
<ClientOnly> <ClientOnly>
<span>{{ currentDate.currentDateTitle }}</span> <span class="text-xs font-medium">{{ currentDate.currentDateTitle }}</span>
<template #fallback> <template #fallback>
<span class="inline-block"> <span class="inline-block">
<UiSkeleton class="h-[19px] w-full rounded-sm" /> <UiSkeleton class="h-[18px] w-full rounded-sm" />
</span> </span>
</template> </template>
</ClientOnly> </ClientOnly>
@@ -157,10 +73,10 @@ function toFutureFar(): void {
<UiButton <UiButton
variant="outline" variant="outline"
size="icon" size="icon"
class="rounded-t-sm rounded-b-none border-b-0" class="size-8 rounded-t-sm rounded-b-none border-b-0"
@click="toPastFar()" @click="toPastFar()"
> >
<PhCaretDoubleLeft size="18" /> <PhCaretDoubleLeft size="14" />
</UiButton> </UiButton>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent> <UiTooltipContent>
@@ -176,10 +92,10 @@ function toFutureFar(): void {
<UiButton <UiButton
variant="outline" variant="outline"
size="icon" size="icon"
class="rounded-t-sm rounded-b-none border-b-0" class="size-8 rounded-t-sm rounded-b-none border-b-0"
@click="toPastNear()" @click="toPastNear()"
> >
<PhCaretLeft size="18" /> <PhCaretLeft size="14" />
</UiButton> </UiButton>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent> <UiTooltipContent>
@@ -195,10 +111,10 @@ function toFutureFar(): void {
<UiButton <UiButton
variant="outline" variant="outline"
size="icon" size="icon"
class="rounded-t-sm rounded-b-none border-b-0" class="size-8 rounded-t-sm rounded-b-none border-b-0"
@click="toFutureNear()" @click="toFutureNear()"
> >
<PhCaretRight size="18" /> <PhCaretRight size="14" />
</UiButton> </UiButton>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent> <UiTooltipContent>
@@ -214,10 +130,10 @@ function toFutureFar(): void {
<UiButton <UiButton
variant="outline" variant="outline"
size="icon" size="icon"
class="rounded-t-sm rounded-b-none border-b-0" class="size-8 rounded-t-sm rounded-b-none border-b-0"
@click="toFutureFar()" @click="toFutureFar()"
> >
<PhCaretDoubleRight size="18" /> <PhCaretDoubleRight size="14" />
</UiButton> </UiButton>
</UiTooltipTrigger> </UiTooltipTrigger>
<UiTooltipContent> <UiTooltipContent>

View File

@@ -26,7 +26,7 @@ const props = withDefaults(defineProps<Props>(), {
<span <span
v-if="props.searchSlash" v-if="props.searchSlash"
class="h-4 p-1 ml-1 grid place-items-center text-[0.7em] leading-none font-semibold text-slate-500 bg-slate-100 border-slate-300 border-[1px] rounded-[3px] shadow-xs group-hover:text-slate-600 group-hover:bg-slate-200 group-hover:border-slate-400 transition-colors" class="h-4 p-1 ml-1 grid place-items-center text-[0.75em] leading-none font-semibold text-slate-500 bg-slate-100 border-slate-300 border-[1px] rounded-[3px] shadow-xs group-hover:text-slate-600 group-hover:bg-slate-200 group-hover:border-slate-400 transition-colors"
> >
<kbd class="-mt-[2px]">CTRL + :</kbd> <kbd class="-mt-[2px]">CTRL + :</kbd>
</span> </span>

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,

View File

@@ -1,3 +1,4 @@
import { defineNuxtConfig } from 'nuxt/config'
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from "@tailwindcss/vite";
// https://nuxt.com/docs/api/configuration/nuxt-config // https://nuxt.com/docs/api/configuration/nuxt-config

View File

@@ -13,57 +13,57 @@
"lint:prettier": "prettier . --check" "lint:prettier": "prettier . --check"
}, },
"dependencies": { "dependencies": {
"@intlify/message-compiler": "^11.1.11", "@intlify/message-compiler": "^11.1.12",
"@nuxt/content": "3.6.3", "@nuxt/content": "3.7.0",
"@nuxt/eslint": "^1.9.0", "@nuxt/eslint": "^1.9.0",
"@nuxt/image": "1.11.0", "@nuxt/image": "1.11.0",
"@nuxthub/core": "^0.9.0", "@nuxthub/core": "^0.9.0",
"@nuxtjs/i18n": "^10.0.6", "@nuxtjs/i18n": "^10.1.0",
"@phosphor-icons/vue": "^2.2.1", "@phosphor-icons/vue": "^2.2.1",
"@pinia/nuxt": "^0.11.2", "@pinia/nuxt": "^0.11.2",
"@tailwindcss/vite": "^4.1.12", "@tailwindcss/vite": "^4.1.14",
"@tiptap/pm": "^3.3.0", "@tiptap/pm": "^3.6.5",
"@tiptap/starter-kit": "^3.3.0", "@tiptap/starter-kit": "^3.6.5",
"@tiptap/vue-3": "^3.3.0", "@tiptap/vue-3": "^3.6.5",
"@vueuse/core": "^13.7.0", "@vueuse/core": "^13.9.0",
"@vueuse/integrations": "^13.7.0", "@vueuse/integrations": "^13.9.0",
"@vueuse/nuxt": "^13.7.0", "@vueuse/nuxt": "^13.9.0",
"better-sqlite3": "^12.2.0", "better-sqlite3": "^12.4.1",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-vue-next": "^0.541.0", "lucide-vue-next": "^0.544.0",
"luxon": "^3.7.1", "luxon": "^3.7.2",
"nuxt": "^4.0.3", "nuxt": "^4.1.3",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"radix-vue": "^1.9.17", "radix-vue": "^1.9.17",
"reka-ui": "^2.4.1", "reka-ui": "^2.5.1",
"shadcn-nuxt": "^2.2.0", "shadcn-nuxt": "^2.3.1",
"sortablejs": "^1.15.6", "sortablejs": "^1.15.6",
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"tw-animate-css": "^1.3.7", "tw-animate-css": "^1.4.0",
"vue": "^3.5.19", "vue": "^3.5.22",
"vue-router": "^4.5.1", "vue-router": "^4.5.1",
"zod": "^4.1.0" "zod": "^4.1.12"
}, },
"devDependencies": { "devDependencies": {
"@nuxtjs/color-mode": "^3.5.2", "@nuxtjs/color-mode": "^3.5.2",
"@nuxtjs/supabase": "^1.6.1", "@nuxtjs/supabase": "^1.6.2",
"@nuxtjs/tailwindcss": "^6.14.0", "@nuxtjs/tailwindcss": "^6.14.0",
"@stylistic/eslint-plugin-js": "^4.4.1", "@stylistic/eslint-plugin-js": "^4.4.1",
"@types/luxon": "^3.7.1", "@types/luxon": "^3.7.1",
"@typescript-eslint/eslint-plugin": "^8.40.0", "@typescript-eslint/eslint-plugin": "^8.46.0",
"@typescript-eslint/parser": "^8.40.0", "@typescript-eslint/parser": "^8.46.0",
"eslint": "^9.34.0", "eslint": "^9.37.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4", "eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-vue": "^10.4.0", "eslint-plugin-vue": "^10.5.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"sass": "^1.90.0", "sass": "^1.93.2",
"supabase": "^2.34.3", "supabase": "^2.48.3",
"tailwindcss": "^4.1.12", "tailwindcss": "^4.1.14",
"typescript": "^5.9.2", "typescript": "^5.9.3",
"wrangler": "^4.32.0" "wrangler": "^4.42.0"
} }
} }

6056
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff