Merge branch 'dev'
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
import { useDark } from '@vueuse/core'
|
||||
import { RouterView } from 'vue-router'
|
||||
|
||||
const dark = useDark()
|
||||
useDark()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { computed } from 'vue'
|
||||
import { computed, type Component, type ComputedRef } from 'vue'
|
||||
|
||||
import CalendarMenu from './CalendarMenu.vue'
|
||||
import Century from './state/Century.vue'
|
||||
import Decade from './state/Decade.vue'
|
||||
import Monthly from './state/Monthly.vue'
|
||||
import Year from './state/Year.vue'
|
||||
|
||||
import MonthlyLayout from './state/monthly/Layout.vue'
|
||||
import CenturyLayout from './state/centennially/Layout.vue'
|
||||
import DecadeLayout from './state/decennially/Layout.vue'
|
||||
import YearLayout from './state/yearly/Layout.vue'
|
||||
|
||||
const { currentConfig } = useCalendar()
|
||||
|
||||
const currentViewComponent = computed(() => {
|
||||
const currentViewComponent: ComputedRef<Component> = computed<Component>(() => {
|
||||
switch (currentConfig.viewType) {
|
||||
case 'month':
|
||||
return Monthly
|
||||
return MonthlyLayout
|
||||
|
||||
case 'year':
|
||||
return Year
|
||||
return YearLayout
|
||||
|
||||
case 'decade':
|
||||
return Decade
|
||||
return DecadeLayout
|
||||
|
||||
case 'century':
|
||||
default:
|
||||
return Century
|
||||
return CenturyLayout
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -30,6 +32,8 @@ const currentViewComponent = computed(() => {
|
||||
<template>
|
||||
<div class="h-full grid grid-rows-[auto,1fr]">
|
||||
<CalendarMenu />
|
||||
<component :is="currentViewComponent" />
|
||||
<KeepAlive>
|
||||
<component :is="currentViewComponent" />
|
||||
</KeepAlive>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
24
src/components/calendar/CalendarCurrentDate.vue
Normal file
24
src/components/calendar/CalendarCurrentDate.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script lang="ts" setup>
|
||||
import { getRelativeString } from '@/models/Date'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { PhMapPin } from '@phosphor-icons/vue'
|
||||
|
||||
const { defaultDate, getFormattedDateTitle } = useCalendar()
|
||||
const { selectedDate } = storeToRefs(useCalendar())
|
||||
|
||||
const mainDateTitle = computed(() => getFormattedDateTitle(selectedDate.value, true))
|
||||
|
||||
const dateDifference = computed(() => getRelativeString(defaultDate, selectedDate.value))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="text-2xl font-bold flex items-center gap-1">
|
||||
<PhMapPin size="26" weight="bold" /> {{ mainDateTitle }}
|
||||
</h1>
|
||||
<h2 class="text-lg italic opacity-75">
|
||||
{{ dateDifference }}
|
||||
</h2>
|
||||
</template>
|
||||
@@ -33,6 +33,6 @@ defineProps<{
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
|
||||
<CalendarEventDetails :event />
|
||||
<CalendarEventDetails :event v-once />
|
||||
</Popover>
|
||||
</template>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { getRelativeString } from '@/models/Date'
|
||||
import type { CalendarEvent } from '@/models/Events'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { PhHourglassMedium } from '@phosphor-icons/vue'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
@@ -12,11 +11,30 @@ const { defaultDate, getFormattedDateTitle } = useCalendar()
|
||||
|
||||
const props = defineProps<{ event: CalendarEvent }>()
|
||||
|
||||
const dateDifference = computed(() => getRelativeString(defaultDate, props.event.date))
|
||||
const dateDifference: string = getRelativeString(defaultDate, props.event.date)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PopoverContent class="w-96" align="start" :align-offset="50" :collision-padding="20">
|
||||
<PopoverContent
|
||||
class="w-96"
|
||||
align="start"
|
||||
:align-offset="50"
|
||||
:collision-padding="20"
|
||||
:class="{
|
||||
'border-slate-800': !event.category,
|
||||
'border-lime-800': event.category === 'naissance',
|
||||
'border-stone-800': event.category === 'mort',
|
||||
'border-orange-800': event.category === 'catastrophe',
|
||||
'border-pink-800': event.category === 'catastrophe naturelle',
|
||||
'border-sky-800': event.category === 'législation',
|
||||
'border-purple-800': event.category === 'religion',
|
||||
'border-emerald-800': event.category === 'joueurs',
|
||||
'border-amber-800': event.category === 'inauguration',
|
||||
'border-green-800': event.category === 'invention',
|
||||
'border-cyan-800': event.category === 'science',
|
||||
'border-yellow-800': event.category === 'bénédiction'
|
||||
}"
|
||||
>
|
||||
<div class="grid gap-1">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ event.title }}
|
||||
@@ -51,3 +69,42 @@ const dateDifference = computed(() => getRelativeString(defaultDate, props.event
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.border-slate-800 {
|
||||
background-color: color-mix(in srgb, var(--color-slate-800), var(--color-slate-950) 85%);
|
||||
}
|
||||
.border-lime-800 {
|
||||
background-color: color-mix(in srgb, var(--color-lime-800), var(--color-slate-950) 85%);
|
||||
}
|
||||
.border-stone-800 {
|
||||
background-color: color-mix(in srgb, var(--color-stone-800), var(--color-slate-950) 85%);
|
||||
}
|
||||
.border-orange-800 {
|
||||
background-color: color-mix(in srgb, var(--color-orange-800), var(--color-slate-950) 85%);
|
||||
}
|
||||
.border-pink-800 {
|
||||
background-color: color-mix(in srgb, var(--color-pink-800), var(--color-slate-950) 85%);
|
||||
}
|
||||
.border-sky-800 {
|
||||
background-color: color-mix(in srgb, var(--color-sky-800), var(--color-slate-950) 85%);
|
||||
}
|
||||
.border-purple-800 {
|
||||
background-color: color-mix(in srgb, var(--color-purple-800), var(--color-slate-950) 85%);
|
||||
}
|
||||
.border-emerald-800 {
|
||||
background-color: color-mix(in srgb, var(--color-emerald-800), var(--color-slate-950) 85%);
|
||||
}
|
||||
.border-amber-800 {
|
||||
background-color: color-mix(in srgb, var(--color-amber-800), var(--color-slate-950) 85%);
|
||||
}
|
||||
.border-green-800 {
|
||||
background-color: color-mix(in srgb, var(--color-green-800), var(--color-slate-950) 85%);
|
||||
}
|
||||
.border-cyan-800 {
|
||||
background-color: color-mix(in srgb, var(--color-cyan-800), var(--color-slate-950) 85%);
|
||||
}
|
||||
.border-yellow-800 {
|
||||
background-color: color-mix(in srgb, var(--color-yellow-800), var(--color-slate-950) 85%);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,60 +1,45 @@
|
||||
<script lang="ts" setup>
|
||||
import { getRelativeString } from '@/models/Date'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { PhMapPin } from '@phosphor-icons/vue'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import CalendarMenuNav from './CalendarMenuNav.vue'
|
||||
import CalendarMenuToday from './CalendarMenuToday.vue'
|
||||
import CalendarSwitch from './CalendarSwitch.vue'
|
||||
import CalendarSearch from './search/CalendarSearch.vue'
|
||||
import CalendarCurrentDate from './CalendarCurrentDate.vue'
|
||||
|
||||
const { defaultDate, getFormattedDateTitle, currentDate } = useCalendar()
|
||||
const { selectedDate } = storeToRefs(useCalendar())
|
||||
|
||||
const mainDateTitle = computed(() => getFormattedDateTitle(selectedDate.value, true))
|
||||
|
||||
const dateDifference = computed(() => getRelativeString(defaultDate, selectedDate.value))
|
||||
const { currentDate, revealAdvancedSearch } = useCalendar()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="border-slate-700 border-b-[1px]">
|
||||
<div class="pt-4 container">
|
||||
<div class="grid md:grid-cols-12 items-center">
|
||||
<div class="md:col-span-9">
|
||||
<div class="flex items-center gap-6">
|
||||
<menu class="flex items-center gap-2">
|
||||
<li>
|
||||
<CalendarSwitch />
|
||||
</li>
|
||||
<li>
|
||||
<CalendarMenuToday />
|
||||
</li>
|
||||
<li>
|
||||
<CalendarMenuNav />
|
||||
</li>
|
||||
</menu>
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold flex items-center gap-1">
|
||||
<PhMapPin size="26" weight="bold" /> {{ mainDateTitle }}
|
||||
</h1>
|
||||
<h2 class="text-lg italic opacity-75">
|
||||
{{ dateDifference }}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md:col-span-3 flex justify-end">
|
||||
<CalendarSearch />
|
||||
</div>
|
||||
</div>
|
||||
<header class="pt-4 border-slate-700 border-b-[1px]">
|
||||
<div class="px-6 flex justify-between">
|
||||
<menu class="flex items-center gap-2">
|
||||
<li class="flex items-center">
|
||||
<CalendarMenuToday />
|
||||
</li>
|
||||
<li>
|
||||
<CalendarMenuNav />
|
||||
</li>
|
||||
<li class="ml-6">
|
||||
<CalendarCurrentDate />
|
||||
</li>
|
||||
</menu>
|
||||
|
||||
<menu class="flex items-center gap-2">
|
||||
<li>
|
||||
<Button search-slash @click="revealAdvancedSearch()">
|
||||
<PhMagnifyingGlass size="20" weight="light" />
|
||||
Recherche avancée
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
<CalendarSwitch />
|
||||
</li>
|
||||
</menu>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="flex">
|
||||
<div class="px-4 py-2 border-slate-700 border-x-[1px] border-t-[1px] rounded-t-sm">
|
||||
<span class="text-sm">{{ currentDate.currentDateTitle }}</span>
|
||||
</div>
|
||||
<div class="ml-12 flex">
|
||||
<div class="px-4 py-2 border-slate-700 border-x-[1px] border-t-[1px] rounded-t-sm">
|
||||
<span class="text-sm">{{ currentDate.currentDateTitle }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -9,22 +9,151 @@ import {
|
||||
PhCaretRight
|
||||
} from '@phosphor-icons/vue'
|
||||
import Button from '../ui/button/Button.vue'
|
||||
import { computed, type ComputedRef } from 'vue'
|
||||
|
||||
const { decrementMonth, incrementMonth, decrementYear, incrementYear } = useCalendar()
|
||||
interface DirectionLabels {
|
||||
pastFar: string
|
||||
pastNear: string
|
||||
futureNear: string
|
||||
futureFar: string
|
||||
}
|
||||
|
||||
const { currentConfig, decrementMonth, incrementMonth, decrementYear, incrementYear } =
|
||||
useCalendar()
|
||||
|
||||
const activeDirectionLabels: ComputedRef<DirectionLabels> = computed(() => {
|
||||
switch (currentConfig.viewType) {
|
||||
case 'month':
|
||||
return {
|
||||
pastFar: 'Année précédente',
|
||||
pastNear: 'Mois précédent',
|
||||
futureNear: 'Mois suivant',
|
||||
futureFar: 'Année suivante'
|
||||
}
|
||||
|
||||
case 'year':
|
||||
return {
|
||||
pastFar: 'Décennie précédente',
|
||||
pastNear: 'Année précédente',
|
||||
futureNear: 'Année suivante',
|
||||
futureFar: 'Décennie suivante'
|
||||
}
|
||||
|
||||
case 'decade':
|
||||
return {
|
||||
pastFar: 'Siècle précédent',
|
||||
pastNear: 'Décennie précédente',
|
||||
futureNear: 'Décennie suivante',
|
||||
futureFar: 'Siècle suivant'
|
||||
}
|
||||
|
||||
case 'century':
|
||||
default:
|
||||
return {
|
||||
pastFar: 'Millénaire précédent',
|
||||
pastNear: 'Siècle précédent',
|
||||
futureNear: 'Siècle suivant',
|
||||
futureFar: 'Millénaire suivant'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function toPastFar(): void {
|
||||
switch (currentConfig.viewType) {
|
||||
case 'month':
|
||||
decrementYear()
|
||||
break
|
||||
|
||||
case 'year':
|
||||
decrementYear(10)
|
||||
break
|
||||
|
||||
case 'decade':
|
||||
decrementYear(100)
|
||||
break
|
||||
|
||||
case 'century':
|
||||
default:
|
||||
decrementYear(1000)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function toPastNear(): void {
|
||||
switch (currentConfig.viewType) {
|
||||
case 'month':
|
||||
decrementMonth()
|
||||
break
|
||||
|
||||
case 'year':
|
||||
decrementYear()
|
||||
break
|
||||
|
||||
case 'decade':
|
||||
decrementYear(10)
|
||||
break
|
||||
|
||||
case 'century':
|
||||
default:
|
||||
decrementYear(100)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function toFutureNear(): void {
|
||||
switch (currentConfig.viewType) {
|
||||
case 'month':
|
||||
incrementMonth()
|
||||
break
|
||||
|
||||
case 'year':
|
||||
incrementYear()
|
||||
break
|
||||
|
||||
case 'decade':
|
||||
incrementYear(10)
|
||||
break
|
||||
|
||||
case 'century':
|
||||
default:
|
||||
incrementYear(100)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function toFutureFar(): void {
|
||||
switch (currentConfig.viewType) {
|
||||
case 'month':
|
||||
incrementYear()
|
||||
break
|
||||
|
||||
case 'year':
|
||||
incrementYear(10)
|
||||
break
|
||||
|
||||
case 'decade':
|
||||
incrementYear(100)
|
||||
break
|
||||
|
||||
case 'century':
|
||||
default:
|
||||
incrementYear(1000)
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- Implement decrementDate to account for other mods -->
|
||||
<TooltipProvider :delayDuration="250">
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button variant="outline" size="icon" @click="decrementYear()">
|
||||
<Button variant="outline" size="icon" @click="toPastFar()">
|
||||
<PhCaretDoubleLeft size="18" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Année précédente</p>
|
||||
<p>{{ activeDirectionLabels.pastFar }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
@@ -32,12 +161,12 @@ const { decrementMonth, incrementMonth, decrementYear, incrementYear } = useCale
|
||||
<TooltipProvider :delayDuration="250">
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button variant="outline" size="icon" @click="decrementMonth()">
|
||||
<Button variant="outline" size="icon" @click="toPastNear()">
|
||||
<PhCaretLeft size="18" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Mois précédent</p>
|
||||
<p>{{ activeDirectionLabels.pastNear }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
@@ -45,12 +174,12 @@ const { decrementMonth, incrementMonth, decrementYear, incrementYear } = useCale
|
||||
<TooltipProvider :delayDuration="250">
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button variant="outline" size="icon" @click="incrementMonth()">
|
||||
<Button variant="outline" size="icon" @click="toFutureNear()">
|
||||
<PhCaretRight size="18" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Mois suivant</p>
|
||||
<p>{{ activeDirectionLabels.futureNear }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
@@ -58,12 +187,12 @@ const { decrementMonth, incrementMonth, decrementYear, incrementYear } = useCale
|
||||
<TooltipProvider :delayDuration="250">
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button variant="outline" size="icon" @click="incrementYear()">
|
||||
<Button variant="outline" size="icon" @click="toFutureFar()">
|
||||
<PhCaretDoubleRight size="18" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Année suivante</p>
|
||||
<p>{{ activeDirectionLabels.futureFar }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { areDatesIdentical } from '@/models/Date'
|
||||
const { defaultDate, selectedDate } = storeToRefs(useCalendar())
|
||||
const { jumpToDefaultDate, getFormattedDateTitle } = useCalendar()
|
||||
|
||||
const defaultDateFormatted = computed(() => getFormattedDateTitle(defaultDate.value, true))
|
||||
const defaultDateFormatted = getFormattedDateTitle(defaultDate.value, true)
|
||||
|
||||
const isDefaultDate = computed(() => {
|
||||
return areDatesIdentical(selectedDate.value, defaultDate.value)
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { areDatesIdentical, type LeimDate } from '@/models/Date'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { useCalendarEvents } from '@/stores/EventStore'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed } from 'vue'
|
||||
import CalendarEvent from './CalendarEvent.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
date: LeimDate
|
||||
faded?: boolean
|
||||
}>()
|
||||
|
||||
const { defaultDate, selectDate } = useCalendar()
|
||||
const { selectedDate } = storeToRefs(useCalendar())
|
||||
const { currentEvents } = storeToRefs(useCalendarEvents())
|
||||
|
||||
const eventsForTheDay = computed(() => {
|
||||
return currentEvents.value.filter((currentEvent) => {
|
||||
return areDatesIdentical(currentEvent.date, props.date)
|
||||
})
|
||||
})
|
||||
|
||||
const isDefaultDate = computed(() => {
|
||||
return areDatesIdentical(props.date, defaultDate)
|
||||
})
|
||||
|
||||
const isSelectedDate = computed(() => {
|
||||
return areDatesIdentical(props.date, selectedDate.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="tile relative text-xs p-2 border-slate-700"
|
||||
:class="{
|
||||
'text-slate-500': props.faded,
|
||||
'text-slate-300': !props.faded
|
||||
}"
|
||||
>
|
||||
<!-- Used for "display all events" -->
|
||||
<button class="absolute inset-0 w-full h-full cursor-default z-0" />
|
||||
|
||||
<button
|
||||
class="relative z-10 group block w-full text-center cursor-pointer"
|
||||
@click="selectDate(date)"
|
||||
>
|
||||
<span
|
||||
class="inline-flex w-8 h-8 aspect-square items-center justify-center rounded-full border-2 border-transparent font-bold transition-colors group-hover:border-slate-800"
|
||||
:class="{
|
||||
'bg-slate-800': isDefaultDate && !isSelectedDate,
|
||||
'text-white bg-blue-500': isSelectedDate
|
||||
}"
|
||||
>
|
||||
{{ date.day }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<ul
|
||||
v-if="eventsForTheDay.length > 0"
|
||||
class="absolute top-12 bottom-2 inset-x-2 grid auto-rows-min gap-1 z-10 pointer-events-none transition-opacity"
|
||||
:class="{
|
||||
'opacity-40': props.faded && !isSelectedDate
|
||||
}"
|
||||
>
|
||||
<li v-for="event in eventsForTheDay" :key="event.title" class="grid pointer-events-auto">
|
||||
<CalendarEvent :event />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tile {
|
||||
&:not(:nth-child(10n)) {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
&:nth-child(n + 11) {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
56
src/components/calendar/Sidebar.vue
Normal file
56
src/components/calendar/Sidebar.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
|
||||
import { PhList, PhHouse, PhMagnifyingGlass } from '@phosphor-icons/vue'
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import Button from '../ui/button/Button.vue'
|
||||
|
||||
const { revealAdvancedSearch } = useCalendar()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="w-16 py-6 border-r-[1px] border-l-slate-500 grid justify-center">
|
||||
<menu class="flex flex-col gap-4">
|
||||
<li class="mb-12">
|
||||
<Button variant="ghost" size="icon" @click="console.log" class="rounded-full">
|
||||
<PhList size="27" />
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
<TooltipProvider :delayDuration="100">
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button variant="ghost" size="icon" class="rounded-full" as-child>
|
||||
<RouterLink to="/">
|
||||
<PhHouse size="24" weight="fill" />
|
||||
</RouterLink>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent :side="'right'">
|
||||
<p>Retourner aux outils</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</li>
|
||||
<li>
|
||||
<TooltipProvider :delayDuration="100">
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="rounded-full"
|
||||
@click="revealAdvancedSearch()"
|
||||
>
|
||||
<PhMagnifyingGlass size="24" weight="fill" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent :side="'right'">
|
||||
<p>Recherche avancée</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</li>
|
||||
</menu>
|
||||
</nav>
|
||||
</template>
|
||||
@@ -62,7 +62,7 @@ import SearchList from './lists/SearchList.vue'
|
||||
const { characters } = useCharacters()
|
||||
const { baseEvents } = useCalendarEvents()
|
||||
|
||||
const modalOpen = ref<boolean>(false)
|
||||
const modalOpen = defineModel({ default: false })
|
||||
|
||||
const searchQuery = ref<string>('')
|
||||
// const searchEnough = computed<boolean>(() => searchQuery.value.length >= 2)
|
||||
@@ -243,13 +243,6 @@ function handleCategorySelect(e: any) {
|
||||
|
||||
<template>
|
||||
<Dialog v-model:open="modalOpen" @update:open="resetSearch()">
|
||||
<DialogTrigger>
|
||||
<Button search-slash>
|
||||
<PhMagnifyingGlass size="20" weight="light" />
|
||||
Recherche avancée
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent
|
||||
class="flex flex-col flex-nowrap top-16 -translate-y-0 data-[state=closed]:slide-out-to-top-[5%] data-[state=open]:slide-in-from-top-[5%]"
|
||||
:class="{
|
||||
@@ -289,10 +282,10 @@ function handleCategorySelect(e: any) {
|
||||
v-model="selectedEntity"
|
||||
@update:model-value="handleEntitySwitch()"
|
||||
>
|
||||
<ToggleGroupItem value="events" aria-label="Uniquement les évènements">
|
||||
<ToggleGroupItem value="events" aria-label="Uniquement les évènements" v-once>
|
||||
Évènements
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="characters" aria-label="Uniquement les personnages">
|
||||
<ToggleGroupItem value="characters" aria-label="Uniquement les personnages" v-once>
|
||||
Personnages
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<template>
|
||||
<div>Annuel</div>
|
||||
</template>
|
||||
139
src/components/calendar/state/monthly/DayTile.vue
Normal file
139
src/components/calendar/state/monthly/DayTile.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<script lang="ts" setup>
|
||||
import { areDatesIdentical, type LeimDate } from '@/models/Date'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { useCalendarEvents } from '@/stores/EventStore'
|
||||
import { useElementBounding } from '@vueuse/core'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, ref, type ComputedRef } from 'vue'
|
||||
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import CalendarEventButton from '../../CalendarEvent.vue'
|
||||
import type { CalendarEvent } from '@/models/Events'
|
||||
|
||||
const props = defineProps<{
|
||||
date: LeimDate
|
||||
faded?: boolean
|
||||
}>()
|
||||
|
||||
const calendarTile = ref()
|
||||
const calendarEventsList = ref()
|
||||
|
||||
const { defaultDate, selectDate } = useCalendar()
|
||||
const { selectedDate } = storeToRefs(useCalendar())
|
||||
const { currentEvents } = storeToRefs(useCalendarEvents())
|
||||
|
||||
const eventsForTheDay = computed(() => {
|
||||
return currentEvents.value.filter((currentEvent) => {
|
||||
return areDatesIdentical(currentEvent.date, props.date)
|
||||
})
|
||||
})
|
||||
|
||||
const isDefaultDate = computed(() => {
|
||||
return areDatesIdentical(props.date, defaultDate)
|
||||
})
|
||||
|
||||
const isSelectedDate = computed(() => {
|
||||
return areDatesIdentical(props.date, selectedDate.value)
|
||||
})
|
||||
|
||||
// Get bounding elements for both tile and events list
|
||||
const { height: tileHeight, top: tileTop } = useElementBounding(calendarTile)
|
||||
const { top: tileListTop } = useElementBounding(calendarEventsList)
|
||||
|
||||
// Compute the available number of events that can be displayed from refs heights
|
||||
const numberOfEventsToFit: ComputedRef<number> = computed(() => {
|
||||
if (!eventsForTheDay.value.length) return 0
|
||||
|
||||
return Math.trunc((tileHeight.value - (tileListTop.value - tileTop.value)) / 40)
|
||||
})
|
||||
|
||||
const eventsToDisplay: ComputedRef<CalendarEvent[]> = computed(() => {
|
||||
return [...eventsForTheDay.value].splice(0, numberOfEventsToFit.value)
|
||||
})
|
||||
const eventsNotDisplayed = computed(
|
||||
() => eventsForTheDay.value.length - eventsToDisplay.value.length
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="calendarTile"
|
||||
class="tile relative text-xs p-2 border-slate-700"
|
||||
:class="{
|
||||
'text-slate-500': props.faded,
|
||||
'text-slate-300': !props.faded
|
||||
}"
|
||||
>
|
||||
<!-- Used for "display all events" -->
|
||||
<button class="absolute inset-0 w-full h-full cursor-default z-0" />
|
||||
|
||||
<button
|
||||
class="relative z-10 group block w-full text-center cursor-pointer"
|
||||
@click="selectDate(date)"
|
||||
>
|
||||
<span
|
||||
class="inline-flex w-8 h-8 aspect-square items-center justify-center rounded-full border-2 border-transparent font-bold transition-colors group-hover:border-slate-800"
|
||||
:class="{
|
||||
'bg-slate-800': isDefaultDate && !isSelectedDate,
|
||||
'text-white bg-blue-500': isSelectedDate
|
||||
}"
|
||||
>
|
||||
{{ date.day }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<ul
|
||||
ref="calendarEventsList"
|
||||
class="absolute top-12 bottom-2 inset-x-2 grid auto-rows-min gap-1 z-10 pointer-events-none transition-opacity"
|
||||
:class="{
|
||||
'opacity-40': props.faded && !isSelectedDate
|
||||
}"
|
||||
>
|
||||
<li v-for="event in eventsToDisplay" :key="event.title" class="grid pointer-events-auto">
|
||||
<CalendarEventButton :event />
|
||||
</li>
|
||||
|
||||
<li v-if="eventsNotDisplayed > 0" class="pointer-events-auto">
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<button
|
||||
class="text-xs px-2 py-1 block w-full text-left font-bold rounded-sm whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer transition-colors hover:bg-slate-800"
|
||||
>
|
||||
{{ eventsNotDisplayed }} autre{{ eventsNotDisplayed > 1 ? 's' : '' }}
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-80" :align="'center'" :side="'right'">
|
||||
<div class="text-center mb-4">
|
||||
<span
|
||||
class="inline-flex w-12 h-12 aspect-square items-center justify-center text-lg font-semibold text-slate-300 bg-slate-800 rounded-full"
|
||||
>
|
||||
{{ date.day }}
|
||||
</span>
|
||||
</div>
|
||||
<ul class="grid auto-rows-min gap-1 z-10 pointer-events-none transition-opacity">
|
||||
<li
|
||||
v-for="event in eventsForTheDay"
|
||||
:key="event.title"
|
||||
class="grid pointer-events-auto"
|
||||
>
|
||||
<CalendarEventButton :event />
|
||||
</li>
|
||||
</ul>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tile {
|
||||
&:not(:nth-child(10n)) {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
&:nth-child(n + 11) {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -2,13 +2,12 @@
|
||||
import type { LeimDate } from '@/models/Date'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { useThrottleFn } from '@vueuse/core'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import CalendarTile from '../CalendarTile.vue'
|
||||
import DayTile from './DayTile.vue'
|
||||
|
||||
const { staticConfig, currentDate, decrementMonth, incrementMonth } = useCalendar()
|
||||
|
||||
const daysPerMonth = computed(() => staticConfig.daysPerMonth)
|
||||
const daysPerMonth = staticConfig.daysPerMonth
|
||||
|
||||
function getNextMonthDate(day: number): LeimDate {
|
||||
let nextDay = day
|
||||
@@ -54,18 +53,17 @@ const moveCalendarRight = useThrottleFn(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="grid" :class="`grid-cols-10`" @wheel="handleWheel">
|
||||
<CalendarTile
|
||||
<div class="grid grid-cols-10" @wheel="handleWheel">
|
||||
<DayTile
|
||||
v-for="day in daysPerMonth"
|
||||
:key="day"
|
||||
:date="{
|
||||
day: day,
|
||||
month: currentDate.currentMonth,
|
||||
year: currentDate.currentYear,
|
||||
period: currentDate.currentPeriod
|
||||
year: currentDate.currentYear
|
||||
}"
|
||||
/>
|
||||
<CalendarTile
|
||||
<DayTile
|
||||
v-for="nextMonthDay in 8"
|
||||
:key="nextMonthDay"
|
||||
faded
|
||||
56
src/components/calendar/state/yearly/DayTile.vue
Normal file
56
src/components/calendar/state/yearly/DayTile.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<script lang="ts" setup>
|
||||
import { areDatesIdentical, type LeimDate } from '@/models/Date'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { useCalendarEvents } from '@/stores/EventStore'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, type ComputedRef } from 'vue'
|
||||
|
||||
const { currentDate, defaultDate, selectDate } = useCalendar()
|
||||
const { selectedDate } = storeToRefs(useCalendar())
|
||||
const { currentEvents } = storeToRefs(useCalendarEvents())
|
||||
|
||||
const props = defineProps<{
|
||||
monthNumber: number
|
||||
dayNumber: number
|
||||
}>()
|
||||
|
||||
const tileDate: ComputedRef<LeimDate> = computed(() => {
|
||||
return {
|
||||
day: props.dayNumber,
|
||||
month: props.monthNumber,
|
||||
year: currentDate.currentYear
|
||||
}
|
||||
})
|
||||
|
||||
const isDefaultDate = computed<boolean>(() => {
|
||||
return areDatesIdentical(tileDate.value, defaultDate)
|
||||
})
|
||||
|
||||
const isSelectedDate = computed<boolean>(() => {
|
||||
return areDatesIdentical(tileDate.value, selectedDate.value)
|
||||
})
|
||||
|
||||
const hasAtLeastOneEvent = computed<boolean>(() => {
|
||||
return Boolean(
|
||||
currentEvents.value.find((currentEvent) => {
|
||||
return areDatesIdentical(currentEvent.date, tileDate.value)
|
||||
})
|
||||
)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="relative grid place-items-center aspect-square rounded-full border-2 border-transparent transition-colors after:content-[''] after:absolute after:top-1 after:right-1 after:w-[.3rem] after:h-[.3rem] after:rounded-full after:transition-colors"
|
||||
:class="{
|
||||
'hover:bg-slate-800 hover:border-slate-800': !isDefaultDate && !isSelectedDate,
|
||||
'bg-slate-800 hover:bg-slate-600 hover:border-slate-600': isDefaultDate && !isSelectedDate,
|
||||
'text-white bg-blue-500 hover:bg-blue-600 hover:border-blue-600': isSelectedDate,
|
||||
'after:bg-green-600': hasAtLeastOneEvent,
|
||||
'after:bg-slate-950': hasAtLeastOneEvent && isSelectedDate
|
||||
}"
|
||||
@click="selectDate(tileDate)"
|
||||
>
|
||||
<span ref="tileRef" class="text-slate-300 text-[.85em]">{{ dayNumber }}</span>
|
||||
</button>
|
||||
</template>
|
||||
37
src/components/calendar/state/yearly/Layout.vue
Normal file
37
src/components/calendar/state/yearly/Layout.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
import { useThrottleFn } from '@vueuse/core'
|
||||
|
||||
import MonthTile from './MonthTile.vue'
|
||||
|
||||
const { staticConfig, decrementYear, incrementYear } = useCalendar()
|
||||
|
||||
function handleWheel(e: WheelEvent) {
|
||||
const isMovingUp = e.deltaY < 0
|
||||
if (isMovingUp) {
|
||||
moveCalendarLeft()
|
||||
} else {
|
||||
moveCalendarRight()
|
||||
}
|
||||
}
|
||||
|
||||
const moveCalendarLeft = useThrottleFn(() => {
|
||||
decrementYear()
|
||||
}, 100)
|
||||
|
||||
const moveCalendarRight = useThrottleFn(() => {
|
||||
incrementYear()
|
||||
}, 100)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container mt-[10vh] mb-auto" @wheel="handleWheel">
|
||||
<div ref="test" class="grid grid-cols-5 gap-x-8 gap-y-16">
|
||||
<MonthTile
|
||||
v-for="month in staticConfig.monthsPerYear"
|
||||
:key="month"
|
||||
:month-number="month - 1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
29
src/components/calendar/state/yearly/MonthTile.vue
Normal file
29
src/components/calendar/state/yearly/MonthTile.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
|
||||
import DayTile from './DayTile.vue'
|
||||
|
||||
const { staticConfig, getMonthName } = useCalendar()
|
||||
|
||||
const props = defineProps<{
|
||||
monthNumber: number
|
||||
}>()
|
||||
|
||||
const tileMonthName: string = getMonthName(props.monthNumber)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="font-medium">
|
||||
{{ tileMonthName }}
|
||||
</div>
|
||||
<div class="grid grid-cols-7">
|
||||
<DayTile
|
||||
v-for="day in staticConfig.daysPerMonth"
|
||||
:key="day"
|
||||
:month-number="monthNumber"
|
||||
:day-number="day"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
29
src/components/ui/card/Card.vue
Normal file
29
src/components/ui/card/Card.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
link?: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn('rounded-lg border bg-card text-card-foreground shadow-sm transition-all', props.class, {
|
||||
'relative outline outline-2 outline-offset-4 outline-transparent hover:bg-sky-950 hover:-translate-y-[.2rem] focus-within:outline-sky-900':
|
||||
props.link
|
||||
})
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
|
||||
<RouterLink
|
||||
v-if="props.link"
|
||||
:to="props.link"
|
||||
class="absolute inset-0 focus-visible:outline-none"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
14
src/components/ui/card/CardContent.vue
Normal file
14
src/components/ui/card/CardContent.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('p-6 pt-0 text-muted-foreground', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
14
src/components/ui/card/CardDescription.vue
Normal file
14
src/components/ui/card/CardDescription.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p :class="cn('text-sm text-muted-foreground mt-1', props.class)">
|
||||
<slot />
|
||||
</p>
|
||||
</template>
|
||||
14
src/components/ui/card/CardFooter.vue
Normal file
14
src/components/ui/card/CardFooter.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex items-center p-6 pt-0', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
14
src/components/ui/card/CardHeader.vue
Normal file
14
src/components/ui/card/CardHeader.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex flex-col gap-y-1.5 px-6 pt-6 pb-3', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
14
src/components/ui/card/CardTitle.vue
Normal file
14
src/components/ui/card/CardTitle.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h3 :class="cn('text-2xl font-semibold leading-none tracking-tight', props.class)">
|
||||
<slot />
|
||||
</h3>
|
||||
</template>
|
||||
6
src/components/ui/card/index.ts
Normal file
6
src/components/ui/card/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export { default as Card } from './Card.vue'
|
||||
export { default as CardHeader } from './CardHeader.vue'
|
||||
export { default as CardTitle } from './CardTitle.vue'
|
||||
export { default as CardDescription } from './CardDescription.vue'
|
||||
export { default as CardContent } from './CardContent.vue'
|
||||
export { default as CardFooter } from './CardFooter.vue'
|
||||
@@ -83,28 +83,28 @@ export const regularEvents: CalendarEvent[] = [
|
||||
},
|
||||
{
|
||||
title: '2ème disparation à Cantane',
|
||||
description: 'Seconde victime de la série de disparitions qui affecte Cantane',
|
||||
description: 'Donovane le mineur kéturien disparait sans laisser de traces.',
|
||||
date: { day: 32, month: 7, year: 3209 },
|
||||
category: 'mort',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: '3ème disparation à Cantane',
|
||||
description: 'Troisième victime de la série de disparitions qui affecte Cantane',
|
||||
description: 'Disparition de Sébastien, gredin sauride.',
|
||||
date: { day: 10, month: 8, year: 3209 },
|
||||
category: 'mort',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: '4ème disparation à Cantane',
|
||||
description: 'Quatrième victime de la série de disparitions qui affecte Cantane',
|
||||
description: 'Disparition de Thérence, patrouilleur sauride de la Vieille Garde.',
|
||||
date: { day: 19, month: 8, year: 3209 },
|
||||
category: 'mort',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
title: '5ème disparation à Cantane',
|
||||
description: 'Cinquième victime de la série de disparitions qui affecte Cantane',
|
||||
description: 'Disparition de Mathilda Boulais, vendeuse de pierres.',
|
||||
date: { day: 22, month: 8, year: 3209 },
|
||||
category: 'mort',
|
||||
hidden: true
|
||||
@@ -114,7 +114,6 @@ export const regularEvents: CalendarEvent[] = [
|
||||
description:
|
||||
'Les artisans et mineurs de Rougefer se réunissent à Cantane pour vendre le fruit de leur dur labeur.',
|
||||
date: { day: 23, month: 8, year: 3209 },
|
||||
category: 'inauguration',
|
||||
hidden: true
|
||||
category: 'inauguration'
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
import HomePage from '@/views/HomePage.vue'
|
||||
import CalendarPage from '@/views/CalendarPage.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView
|
||||
name: 'accueil',
|
||||
component: HomePage
|
||||
},
|
||||
{
|
||||
path: '/calendrier',
|
||||
name: 'calendrier',
|
||||
component: CalendarPage
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
@@ -89,22 +89,10 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
return {
|
||||
day: defaultDay,
|
||||
month: defaultMonth,
|
||||
year: defaultYear,
|
||||
period: 'nante'
|
||||
year: defaultYear
|
||||
}
|
||||
})
|
||||
|
||||
// Assign default values if no params exist in URL
|
||||
if (!params.day || typeof params.day === 'object' || !isInt(params.day)) {
|
||||
params.day = defaultDay.toString()
|
||||
}
|
||||
if (!params.month || typeof params.month === 'object' || !isDigit(params.month)) {
|
||||
params.month = defaultMonth.toString()
|
||||
}
|
||||
if (!params.year || typeof params.year === 'object' || !isSignedInt(params.year)) {
|
||||
params.year = defaultYear.toString()
|
||||
}
|
||||
|
||||
const currentDay = computed<number>(() => {
|
||||
return Number(params.day)
|
||||
})
|
||||
@@ -192,6 +180,19 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
}
|
||||
})
|
||||
|
||||
function validateUrlParams(): void {
|
||||
// Assign default values if no params exist in URL
|
||||
if (!params.day || typeof params.day === 'object' || !isInt(params.day)) {
|
||||
params.day = defaultDate.value.day.toString()
|
||||
}
|
||||
if (!params.month || typeof params.month === 'object' || !isDigit(params.month)) {
|
||||
params.month = defaultDate.value.month.toString()
|
||||
}
|
||||
if (!params.year || typeof params.year === 'object' || !isSignedInt(params.year)) {
|
||||
params.year = defaultDate.value.year.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the current date forward one month
|
||||
*/
|
||||
@@ -378,7 +379,6 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
*/
|
||||
function jumpToDefaultDate(): void {
|
||||
jumpToDate(defaultDate.value)
|
||||
currentConfig.value.viewType = 'month'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,6 +388,12 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
selectedDate.value = date
|
||||
}
|
||||
|
||||
const isAdvancedSearchOpen: Ref<boolean> = ref<boolean>(false)
|
||||
|
||||
function revealAdvancedSearch() {
|
||||
isAdvancedSearchOpen.value = true
|
||||
}
|
||||
|
||||
return {
|
||||
staticConfig,
|
||||
viewTypeOptions,
|
||||
@@ -398,6 +404,7 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
selectedDate,
|
||||
selectDate,
|
||||
params,
|
||||
validateUrlParams,
|
||||
getPeriodOfYear,
|
||||
incrementMonth,
|
||||
decrementMonth,
|
||||
@@ -412,6 +419,8 @@ export const useCalendar = defineStore('calendar', () => {
|
||||
getMonthName,
|
||||
setViewType,
|
||||
getViewTypeTitle,
|
||||
isCurrentScreenActive
|
||||
isCurrentScreenActive,
|
||||
isAdvancedSearchOpen,
|
||||
revealAdvancedSearch
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import { charactersList } from '@/data/Characters'
|
||||
import type { Character } from '@/models/Characters'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, type ComputedRef } from 'vue'
|
||||
|
||||
export const useCharacters = defineStore('characters', () => {
|
||||
const characters: Character[] = charactersList
|
||||
|
||||
// Get all birth events
|
||||
const charactersWithBirthData: ComputedRef<Character[]> = computed(() =>
|
||||
characters.filter((character) => character.birth)
|
||||
)
|
||||
const charactersWithBirthData: Character[] = characters.filter((character) => character.birth)
|
||||
|
||||
// Get all death events
|
||||
const charactersWithDeathData: ComputedRef<Character[]> = computed(() =>
|
||||
characters.filter((character) => character.death)
|
||||
)
|
||||
const charactersWithDeathData: Character[] = characters.filter((character) => character.death)
|
||||
|
||||
return { characters, charactersWithBirthData, charactersWithDeathData }
|
||||
})
|
||||
|
||||
@@ -1,40 +1,34 @@
|
||||
import type { CalendarEvent } from '@/models/Events'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref, watch, type Ref } from 'vue'
|
||||
import { useCalendar } from './CalendarStore'
|
||||
import { useCharacters } from './CharacterStore'
|
||||
import { regularEvents } from '@/data/Events'
|
||||
import { convertDateToDays, daysPerMonth } from '@/models/Date'
|
||||
import type { CalendarEvent } from '@/models/Events'
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, watch, type Ref } from 'vue'
|
||||
import { useCalendar } from './CalendarStore'
|
||||
import { useCharacters } from './CharacterStore'
|
||||
|
||||
export const useCalendarEvents = defineStore('calendar-events', () => {
|
||||
const { currentDate, currentConfig, currentLeimDate } = useCalendar()
|
||||
const { currentDate, currentConfig } = useCalendar()
|
||||
const { charactersWithBirthData, charactersWithDeathData } = useCharacters()
|
||||
|
||||
const baseEvents = ref<CalendarEvent[]>(regularEvents)
|
||||
const baseEvents: CalendarEvent[] = regularEvents
|
||||
|
||||
const characterBirthEvents = computed(() => {
|
||||
return charactersWithBirthData.map((character) => {
|
||||
return {
|
||||
title: `Naissance de ${character.name}`,
|
||||
date: character.birth,
|
||||
category: 'naissance'
|
||||
} as CalendarEvent
|
||||
})
|
||||
const characterBirthEvents = charactersWithBirthData.map((character) => {
|
||||
return {
|
||||
title: `Naissance de ${character.name}`,
|
||||
date: character.birth,
|
||||
category: 'naissance'
|
||||
} as CalendarEvent
|
||||
})
|
||||
|
||||
const characterDeathEvents = computed(() => {
|
||||
return charactersWithDeathData.map((character) => {
|
||||
return {
|
||||
title: `Décès de ${character.name}`,
|
||||
date: character.death,
|
||||
category: 'mort'
|
||||
} as CalendarEvent
|
||||
})
|
||||
const characterDeathEvents = charactersWithDeathData.map((character) => {
|
||||
return {
|
||||
title: `Décès de ${character.name}`,
|
||||
date: character.death,
|
||||
category: 'mort'
|
||||
} as CalendarEvent
|
||||
})
|
||||
|
||||
const allEvents = computed(() => {
|
||||
return [...characterBirthEvents.value, ...characterDeathEvents.value, ...baseEvents.value]
|
||||
})
|
||||
const allEvents = [...characterBirthEvents, ...characterDeathEvents, ...baseEvents]
|
||||
|
||||
// Gets all current event in its default state
|
||||
const currentEvents: Ref<CalendarEvent[]> = ref(computeCurrentEvents())
|
||||
@@ -100,7 +94,7 @@ export const useCalendarEvents = defineStore('calendar-events', () => {
|
||||
* @returns A list of events that can appear in the current view
|
||||
*/
|
||||
function computeCurrentEvents(): CalendarEvent[] {
|
||||
return allEvents.value.filter((event) => shouldEventBeDisplayed(event))
|
||||
return allEvents.filter((event) => shouldEventBeDisplayed(event))
|
||||
}
|
||||
|
||||
return { baseEvents, allEvents, currentEvents }
|
||||
|
||||
23
src/views/CalendarPage.vue
Normal file
23
src/views/CalendarPage.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script lang="ts" setup>
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { onMounted } from 'vue'
|
||||
import { useCalendar } from '@/stores/CalendarStore'
|
||||
|
||||
import Calendar from '@/components/calendar/Calendar.vue'
|
||||
import Sidebar from '@/components/calendar/Sidebar.vue'
|
||||
import CalendarSearch from '@/components/calendar/search/CalendarSearch.vue'
|
||||
|
||||
const { validateUrlParams } = useCalendar()
|
||||
const { isAdvancedSearchOpen } = storeToRefs(useCalendar())
|
||||
|
||||
onMounted(() => validateUrlParams())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="h-full grid grid-cols-[auto_1fr]">
|
||||
<Sidebar />
|
||||
<Calendar />
|
||||
</main>
|
||||
|
||||
<CalendarSearch v-model:model-value="isAdvancedSearchOpen" />
|
||||
</template>
|
||||
22
src/views/HomePage.vue
Normal file
22
src/views/HomePage.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="h-full grid place-items-center" v-once>
|
||||
<Card class="w-1/3 min-w-72" link="/calendrier">
|
||||
<CardHeader>
|
||||
<CardTitle>Calendrier</CardTitle>
|
||||
<CardDescription>
|
||||
<Badge class="mix-blend-luminosity font-bold bg-gray-600" variant="secondary">
|
||||
chronologie
|
||||
</Badge>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
Chronologie complète de Léïm, rassemblant les évènements et personnages clés du monde
|
||||
</CardContent>
|
||||
</Card>
|
||||
</main>
|
||||
</template>
|
||||
@@ -1,49 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import Calendar from '@/components/calendar/Calendar.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="h-full">
|
||||
<Calendar />
|
||||
<!-- <pre>
|
||||
{{ currentDate }}
|
||||
</pre>
|
||||
<div class="grid gap-2">
|
||||
<div class="flex gap-2">
|
||||
<button class="bg-slate-800 py-2 px-4 rounded-sm" @click="incrementMonth()">Month +</button>
|
||||
<button class="bg-slate-800 py-2 px-4 rounded-sm" @click="decrementMonth()">Month -</button>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button class="bg-slate-800 py-2 px-4 rounded-sm" @click="incrementYear()">Year +</button>
|
||||
<button class="bg-slate-800 py-2 px-4 rounded-sm" @click="decrementYear()">Year -</button>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button class="bg-slate-800 py-2 px-4 rounded-sm" @click="incrementYear(100)">
|
||||
Century +
|
||||
</button>
|
||||
<button class="bg-slate-800 py-2 px-4 rounded-sm" @click="decrementYear(100)">
|
||||
Century -
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button class="bg-slate-800 py-2 px-4 rounded-sm" @click="incrementYear(1000)">
|
||||
Millenia +
|
||||
</button>
|
||||
<button class="bg-slate-800 py-2 px-4 rounded-sm" @click="decrementYear(1000)">
|
||||
Millenia -
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<input class="bg-slate-900" type="number" v-model="monthTarget" />
|
||||
<button class="bg-slate-800 py-2 px-4 rounded-sm" @click="setMonth(monthTarget)">
|
||||
Set Month
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pre>
|
||||
Current events:
|
||||
{{ currentEvents }}
|
||||
</pre> -->
|
||||
</main>
|
||||
</template>
|
||||
@@ -89,5 +89,25 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [animate]
|
||||
plugins: [
|
||||
animate,
|
||||
function ({ addBase, theme }) {
|
||||
function extractColorVars(colorObj, colorGroup = '') {
|
||||
return Object.keys(colorObj).reduce((vars, colorKey) => {
|
||||
const value = colorObj[colorKey]
|
||||
|
||||
const newVars =
|
||||
typeof value === 'string'
|
||||
? { [`--color${colorGroup}-${colorKey}`]: value }
|
||||
: extractColorVars(value, `-${colorKey}`)
|
||||
|
||||
return { ...vars, ...newVars }
|
||||
}, {})
|
||||
}
|
||||
|
||||
addBase({
|
||||
':root': extractColorVars(theme('colors'))
|
||||
})
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user