Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7443769f00 | ||
|
|
692aaad868 | ||
|
|
371bb7e38a | ||
|
|
c4e70eb29c | ||
|
|
f8d2387b59 | ||
|
|
8c53616c79 | ||
|
|
8dff0763e9 | ||
|
|
9c7b503ca8 |
@@ -1,6 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCalendar } from "@/stores/CalendarStore"
|
||||
import { computed, type Component, type ComputedRef } from "vue"
|
||||
|
||||
// import { PhMagnifyingGlass } from '@phosphor-icons/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 YearLayout from "./state/yearly/Layout.vue"
|
||||
|
||||
const { currentConfig, jumpToDate, selectedDate } = useCalendar()
|
||||
const { isReadOnly } = storeToRefs(useCalendar())
|
||||
import { onKeyDown } from "@vueuse/core"
|
||||
|
||||
const { currentConfig, jumpToDate, selectedDate, toPastFar, toPastNear, toFutureNear, toFutureFar, toggleCreatingEventModal } = useCalendar()
|
||||
const { isReadOnly, calendarState } = storeToRefs(useCalendar())
|
||||
|
||||
const currentViewComponent: ComputedRef<Component> = computed<Component>(() => {
|
||||
switch (currentConfig.viewType) {
|
||||
@@ -31,6 +32,45 @@ const currentViewComponent: ComputedRef<Component> = computed<Component>(() => {
|
||||
onMounted(() => {
|
||||
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>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -3,20 +3,8 @@ import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
|
||||
import { PhPlus } from "@phosphor-icons/vue";
|
||||
import { VisuallyHidden } from "radix-vue";
|
||||
|
||||
const isDialogOpen = ref<boolean>(false);
|
||||
const { resetSkeleton } = useCalendar();
|
||||
|
||||
// Watch the popover state
|
||||
watch(isDialogOpen, (hasOpened, _o) => {
|
||||
if (hasOpened) {
|
||||
resetSkeleton()
|
||||
}
|
||||
})
|
||||
|
||||
// Toggles the dialog
|
||||
function toggleDialog() {
|
||||
isDialogOpen.value = !isDialogOpen.value;
|
||||
};
|
||||
const { isCreatingEventModalOpen } = storeToRefs(useCalendar());
|
||||
const { resetSkeleton, toggleCreatingEventModal } = useCalendar();
|
||||
|
||||
/**
|
||||
* Prevents the modal from closing if's still loading
|
||||
@@ -36,7 +24,7 @@ const breakpoints = useBreakpoints(
|
||||
<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"
|
||||
:size="breakpoints.md.value ? 'default' : 'icon'"
|
||||
@click="toggleDialog"
|
||||
@click="toggleCreatingEventModal"
|
||||
>
|
||||
<PhPlus :size="breakpoints.md.value ? 18 : 24" weight="bold" />
|
||||
|
||||
@@ -47,7 +35,7 @@ const breakpoints = useBreakpoints(
|
||||
</Transition>
|
||||
</ClientOnly>
|
||||
|
||||
<UiDialog v-model:open="isDialogOpen">
|
||||
<UiDialog v-model:open="isCreatingEventModalOpen">
|
||||
<UiDialogContent
|
||||
class="max-md:translate-0 max-md:inset-0 max-md:w-full max-md:block"
|
||||
:trap-focus="true"
|
||||
@@ -64,7 +52,7 @@ const breakpoints = useBreakpoints(
|
||||
</UiDialogDescription>
|
||||
</VisuallyHidden>
|
||||
|
||||
<CalendarFormCreateEvent @event-created="toggleDialog" />
|
||||
<CalendarFormCreateEvent @event-created="toggleCreatingEventModal" />
|
||||
</UiDialogContent>
|
||||
</UiDialog>
|
||||
</template>
|
||||
|
||||
@@ -12,7 +12,7 @@ interface DirectionLabels {
|
||||
futureFar: string
|
||||
}
|
||||
|
||||
const { currentConfig, decrementViewMonth, incrementViewMonth, decrementViewYear, incrementViewYear } =
|
||||
const { currentConfig, toPastFar, toPastNear, toFutureNear, toFutureFar } =
|
||||
useCalendar()
|
||||
|
||||
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>
|
||||
|
||||
<template>
|
||||
<div class="flex gap-2">
|
||||
<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="flex gap-1">
|
||||
<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>
|
||||
<span>{{ currentDate.currentDateTitle }}</span>
|
||||
<span class="text-xs font-medium">{{ currentDate.currentDateTitle }}</span>
|
||||
|
||||
<template #fallback>
|
||||
<span class="inline-block">
|
||||
<UiSkeleton class="h-[19px] w-full rounded-sm" />
|
||||
<UiSkeleton class="h-[18px] w-full rounded-sm" />
|
||||
</span>
|
||||
</template>
|
||||
</ClientOnly>
|
||||
@@ -157,10 +73,10 @@ function toFutureFar(): void {
|
||||
<UiButton
|
||||
variant="outline"
|
||||
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()"
|
||||
>
|
||||
<PhCaretDoubleLeft size="18" />
|
||||
<PhCaretDoubleLeft size="14" />
|
||||
</UiButton>
|
||||
</UiTooltipTrigger>
|
||||
<UiTooltipContent>
|
||||
@@ -176,10 +92,10 @@ function toFutureFar(): void {
|
||||
<UiButton
|
||||
variant="outline"
|
||||
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()"
|
||||
>
|
||||
<PhCaretLeft size="18" />
|
||||
<PhCaretLeft size="14" />
|
||||
</UiButton>
|
||||
</UiTooltipTrigger>
|
||||
<UiTooltipContent>
|
||||
@@ -195,10 +111,10 @@ function toFutureFar(): void {
|
||||
<UiButton
|
||||
variant="outline"
|
||||
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()"
|
||||
>
|
||||
<PhCaretRight size="18" />
|
||||
<PhCaretRight size="14" />
|
||||
</UiButton>
|
||||
</UiTooltipTrigger>
|
||||
<UiTooltipContent>
|
||||
@@ -214,10 +130,10 @@ function toFutureFar(): void {
|
||||
<UiButton
|
||||
variant="outline"
|
||||
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()"
|
||||
>
|
||||
<PhCaretDoubleRight size="18" />
|
||||
<PhCaretDoubleRight size="14" />
|
||||
</UiButton>
|
||||
</UiTooltipTrigger>
|
||||
<UiTooltipContent>
|
||||
@@ -26,7 +26,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
<span
|
||||
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>
|
||||
</span>
|
||||
|
||||
@@ -10,6 +10,8 @@ import type { CalendarEvent } from "@@/models/CalendarEvent"
|
||||
import type { CalendarMonth } from "@@/models/CalendarMonth"
|
||||
import type { Category } from "@@/models/Category"
|
||||
|
||||
type CalendarState = "active" | "idle"
|
||||
|
||||
type CalendarViewType = "month" | "year" | "decade" | "century"
|
||||
|
||||
type CalendarCurrentConfig = {
|
||||
@@ -668,6 +670,90 @@ export const useCalendar = defineStore("calendar", () => {
|
||||
currentEvents.value = computeCurrentEvents()
|
||||
}, { 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
|
||||
*
|
||||
@@ -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
|
||||
*/
|
||||
@@ -936,6 +1038,19 @@ export const useCalendar = defineStore("calendar", () => {
|
||||
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 {
|
||||
isReadOnly,
|
||||
setReadStatus,
|
||||
@@ -975,10 +1090,15 @@ export const useCalendar = defineStore("calendar", () => {
|
||||
areDatesIdentical,
|
||||
compareDates,
|
||||
getRelativeString,
|
||||
toPastFar,
|
||||
toPastNear,
|
||||
toFutureNear,
|
||||
toFutureFar,
|
||||
baseEvents,
|
||||
allEvents,
|
||||
categories,
|
||||
currentEvents,
|
||||
calendarState,
|
||||
getRelativeEventFromDate,
|
||||
getRelativeEventFromEvent,
|
||||
cancelLatestRequest,
|
||||
@@ -993,6 +1113,8 @@ export const useCalendar = defineStore("calendar", () => {
|
||||
lastActiveEvent,
|
||||
updateEventFromSkeleton,
|
||||
deleteEventFromSkeleton,
|
||||
isCreatingEventModalOpen,
|
||||
toggleCreatingEventModal,
|
||||
isEditEventModalOpen,
|
||||
revealEditEventModal,
|
||||
isDeleteEventModalOpen,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { defineNuxtConfig } from 'nuxt/config'
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
|
||||
58
package.json
58
package.json
@@ -13,57 +13,57 @@
|
||||
"lint:prettier": "prettier . --check"
|
||||
},
|
||||
"dependencies": {
|
||||
"@intlify/message-compiler": "^11.1.11",
|
||||
"@nuxt/content": "3.6.3",
|
||||
"@intlify/message-compiler": "^11.1.12",
|
||||
"@nuxt/content": "3.7.0",
|
||||
"@nuxt/eslint": "^1.9.0",
|
||||
"@nuxt/image": "1.11.0",
|
||||
"@nuxthub/core": "^0.9.0",
|
||||
"@nuxtjs/i18n": "^10.0.6",
|
||||
"@nuxtjs/i18n": "^10.1.0",
|
||||
"@phosphor-icons/vue": "^2.2.1",
|
||||
"@pinia/nuxt": "^0.11.2",
|
||||
"@tailwindcss/vite": "^4.1.12",
|
||||
"@tiptap/pm": "^3.3.0",
|
||||
"@tiptap/starter-kit": "^3.3.0",
|
||||
"@tiptap/vue-3": "^3.3.0",
|
||||
"@vueuse/core": "^13.7.0",
|
||||
"@vueuse/integrations": "^13.7.0",
|
||||
"@vueuse/nuxt": "^13.7.0",
|
||||
"better-sqlite3": "^12.2.0",
|
||||
"@tailwindcss/vite": "^4.1.14",
|
||||
"@tiptap/pm": "^3.6.5",
|
||||
"@tiptap/starter-kit": "^3.6.5",
|
||||
"@tiptap/vue-3": "^3.6.5",
|
||||
"@vueuse/core": "^13.9.0",
|
||||
"@vueuse/integrations": "^13.9.0",
|
||||
"@vueuse/nuxt": "^13.9.0",
|
||||
"better-sqlite3": "^12.4.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-vue-next": "^0.541.0",
|
||||
"luxon": "^3.7.1",
|
||||
"nuxt": "^4.0.3",
|
||||
"lucide-vue-next": "^0.544.0",
|
||||
"luxon": "^3.7.2",
|
||||
"nuxt": "^4.1.3",
|
||||
"pinia": "^3.0.3",
|
||||
"radix-vue": "^1.9.17",
|
||||
"reka-ui": "^2.4.1",
|
||||
"shadcn-nuxt": "^2.2.0",
|
||||
"reka-ui": "^2.5.1",
|
||||
"shadcn-nuxt": "^2.3.1",
|
||||
"sortablejs": "^1.15.6",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tw-animate-css": "^1.3.7",
|
||||
"vue": "^3.5.19",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"vue": "^3.5.22",
|
||||
"vue-router": "^4.5.1",
|
||||
"zod": "^4.1.0"
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxtjs/color-mode": "^3.5.2",
|
||||
"@nuxtjs/supabase": "^1.6.1",
|
||||
"@nuxtjs/supabase": "^1.6.2",
|
||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||
"@stylistic/eslint-plugin-js": "^4.4.1",
|
||||
"@types/luxon": "^3.7.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.40.0",
|
||||
"@typescript-eslint/parser": "^8.40.0",
|
||||
"eslint": "^9.34.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.46.0",
|
||||
"@typescript-eslint/parser": "^8.46.0",
|
||||
"eslint": "^9.37.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-vue": "^10.4.0",
|
||||
"eslint-plugin-vue": "^10.5.0",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "^3.6.2",
|
||||
"sass": "^1.90.0",
|
||||
"supabase": "^2.34.3",
|
||||
"tailwindcss": "^4.1.12",
|
||||
"typescript": "^5.9.2",
|
||||
"wrangler": "^4.32.0"
|
||||
"sass": "^1.93.2",
|
||||
"supabase": "^2.48.3",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"typescript": "^5.9.3",
|
||||
"wrangler": "^4.42.0"
|
||||
}
|
||||
}
|
||||
|
||||
6056
pnpm-lock.yaml
generated
6056
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user