Migration to nuxt 4
Used codemods CLI and reworked most alias'd path that stopped working
This commit is contained in:
194
app/components/calendar/state/monthly/DayTile.vue
Normal file
194
app/components/calendar/state/monthly/DayTile.vue
Normal file
@@ -0,0 +1,194 @@
|
||||
<script lang="ts" setup>
|
||||
import type { RPGDate } from "@@/models/Date"
|
||||
import type { CalendarEvent } from "@@/models/CalendarEvent"
|
||||
import { breakpointsTailwind, useElementBounding } from "@vueuse/core"
|
||||
import { storeToRefs } from "pinia"
|
||||
import { computed, ref, type ComputedRef } from "vue"
|
||||
|
||||
import CalendarEventButton from "../../event/Event.vue"
|
||||
|
||||
const props = defineProps<{
|
||||
date: RPGDate
|
||||
faded?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
"on-open-create-dialog": [date: RPGDate]
|
||||
}>()
|
||||
|
||||
const calendarTile = ref()
|
||||
const calendarEventsList = ref()
|
||||
|
||||
const { defaultDate, selectDate, areDatesIdentical, getFormattedDateTitle } = useCalendar()
|
||||
const { selectedDate, currentEvents, isReadOnly } = storeToRefs(useCalendar())
|
||||
|
||||
const breakpoints = useBreakpoints(
|
||||
breakpointsTailwind
|
||||
)
|
||||
|
||||
/**
|
||||
* All events with a startDate / endDate that starts or ends on the tile
|
||||
*/
|
||||
const eventsForTheDay = computed(() => {
|
||||
return currentEvents.value.filter((currentEvent) => {
|
||||
return (
|
||||
areDatesIdentical(currentEvent.startDate, props.date) ||
|
||||
areDatesIdentical(currentEvent.endDate!, props.date)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Is the tile on today's date ?
|
||||
*/
|
||||
const isDefaultDate = computed(() => {
|
||||
return areDatesIdentical(props.date, defaultDate)
|
||||
})
|
||||
|
||||
/**
|
||||
* Is the tile on the hightlighted date ?
|
||||
*/
|
||||
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)
|
||||
})
|
||||
|
||||
/**
|
||||
* Events that can fit in the tile's space
|
||||
*/
|
||||
const eventsToDisplay: ComputedRef<CalendarEvent[]> = computed<CalendarEvent[]>(() => {
|
||||
return [...eventsForTheDay.value].splice(0, numberOfEventsToFit.value)
|
||||
})
|
||||
|
||||
/**
|
||||
* Remaining events that are not displayed
|
||||
*
|
||||
* Used if not all events can be seen in the tile's space
|
||||
*/
|
||||
const eventsNotDisplayed: ComputedRef<number> = computed<number>(() => eventsForTheDay.value.length - eventsToDisplay.value.length)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="calendarTile"
|
||||
class="tile relative p-1 md:p-2 transition-colors"
|
||||
:class="{
|
||||
'text-slate-300 dark:text-slate-500': props.faded,
|
||||
'text-slate-500 dark:text-slate-300': !props.faded
|
||||
}"
|
||||
>
|
||||
<button
|
||||
class="relative z-10 group block w-full text-2xs md:text-xs text-center cursor-pointer"
|
||||
@click="selectDate(date)"
|
||||
>
|
||||
<ClientOnly>
|
||||
<span
|
||||
class="inline-flex size-7 md:size-8 aspect-square items-center justify-center rounded-full border-1 md:border-2 border-transparent font-bold transition-colors group-hover:border-indigo-300 dark:group-hover:border-indigo-700"
|
||||
:class="{
|
||||
'text-white bg-slate-600 dark:bg-slate-800': isDefaultDate && !isSelectedDate,
|
||||
'text-white bg-indigo-500': isSelectedDate
|
||||
}"
|
||||
>
|
||||
{{ date.day }}
|
||||
</span>
|
||||
</ClientOnly>
|
||||
</button>
|
||||
|
||||
<ClientOnly>
|
||||
<ul
|
||||
ref="calendarEventsList"
|
||||
class="absolute top-9 md:top-12 bottom-1 md:bottom-2 inset-x-2 grid auto-rows-min gap-1 z-10 pointer-events-none transition-opacity"
|
||||
:class="{
|
||||
'opacity-40': props.faded && !isSelectedDate
|
||||
}"
|
||||
>
|
||||
<TransitionGroup name="event">
|
||||
<li v-for="event in eventsToDisplay" :key="event.id" class="grid w-full pointer-events-auto">
|
||||
<CalendarEventButton :event :tile-date="date" />
|
||||
</li>
|
||||
</TransitionGroup>
|
||||
|
||||
<li v-if="eventsNotDisplayed > 0" class="pointer-events-auto">
|
||||
<UiPopover>
|
||||
<UiPopoverTrigger as-child>
|
||||
<button
|
||||
class="text-2xs md:text-xs px-[5px] py-[5px] md:px-2 md:py-1 block w-full text-left font-bold rounded-sm whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer transition-colors hover:text-foreground hover:bg-foreground/10"
|
||||
>
|
||||
{{ eventsNotDisplayed }} autre{{ eventsNotDisplayed > 1 ? 's' : '' }}
|
||||
</button>
|
||||
</UiPopoverTrigger>
|
||||
<UiPopoverContent
|
||||
class="w-80"
|
||||
:align="'center'"
|
||||
:align-offset="breakpoints.lg.value ? 50 : 25"
|
||||
:collision-padding="breakpoints.lg.value ? 50 : 25"
|
||||
:side="breakpoints.lg.value ? 'left' : 'bottom'"
|
||||
>
|
||||
<div class="text-center mb-2">
|
||||
<span
|
||||
class="text-lg font-semibold rounded-full"
|
||||
>
|
||||
{{ getFormattedDateTitle(date, true) }}
|
||||
</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 :tile-date="date" />
|
||||
</li>
|
||||
</ul>
|
||||
</UiPopoverContent>
|
||||
</UiPopover>
|
||||
</li>
|
||||
</ul>
|
||||
</ClientOnly>
|
||||
|
||||
<ClientOnly>
|
||||
<LazyCalendarDialogCreateEvent v-if="!isReadOnly && breakpoints.lg.value" :date btn-class="absolute inset-0 w-full h-full cursor-default z-0" />
|
||||
<button
|
||||
v-else-if="!isReadOnly && !breakpoints.lg.value"
|
||||
class="absolute inset-0 w-full h-full cursor-default z-0"
|
||||
@click="emit('on-open-create-dialog', props.date)"
|
||||
/>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tile {
|
||||
border-right-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.event-enter-active {
|
||||
transition: all 120ms ease-in-out;
|
||||
}
|
||||
.event-move,
|
||||
.event-leave-active {
|
||||
position: absolute;
|
||||
transition: all 200ms ease-in-out;
|
||||
}
|
||||
.event-enter-from,
|
||||
.event-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.event-enter-from {
|
||||
transform: translateX(.15rem);
|
||||
}
|
||||
.event-leave-to {
|
||||
transform: translateX(-.5rem);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user