mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 15:36:49 +08:00
feat: 添加日历事件展开功能,优化事件显示和用户交互体验
This commit is contained in:
@@ -1194,6 +1194,7 @@ export default {
|
|||||||
currentEpisodeNotInLibrary: 'Current not in library',
|
currentEpisodeNotInLibrary: 'Current not in library',
|
||||||
libraryUpdatedAt: 'Updated {time}',
|
libraryUpdatedAt: 'Updated {time}',
|
||||||
libraryUpdatedAtShort: '{time}',
|
libraryUpdatedAtShort: '{time}',
|
||||||
|
expandDayEvents: 'Show {count} more items for this day',
|
||||||
},
|
},
|
||||||
storage: {
|
storage: {
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
|
|||||||
@@ -1189,6 +1189,7 @@ export default {
|
|||||||
currentEpisodeNotInLibrary: '本集未入库',
|
currentEpisodeNotInLibrary: '本集未入库',
|
||||||
libraryUpdatedAt: '最近更新 {time}',
|
libraryUpdatedAt: '最近更新 {time}',
|
||||||
libraryUpdatedAtShort: '{time}',
|
libraryUpdatedAtShort: '{time}',
|
||||||
|
expandDayEvents: '展开当天剩余 {count} 个条目',
|
||||||
},
|
},
|
||||||
storage: {
|
storage: {
|
||||||
name: '名称',
|
name: '名称',
|
||||||
|
|||||||
@@ -1189,6 +1189,7 @@ export default {
|
|||||||
currentEpisodeNotInLibrary: '本集未入庫',
|
currentEpisodeNotInLibrary: '本集未入庫',
|
||||||
libraryUpdatedAt: '最近更新 {time}',
|
libraryUpdatedAt: '最近更新 {time}',
|
||||||
libraryUpdatedAtShort: '{time}',
|
libraryUpdatedAtShort: '{time}',
|
||||||
|
expandDayEvents: '展開當天剩餘 {count} 個條目',
|
||||||
},
|
},
|
||||||
storage: {
|
storage: {
|
||||||
name: '名稱',
|
name: '名稱',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { CalendarOptions, EventSourceInput } from '@fullcalendar/core'
|
import type { CalendarOptions, EventInput, EventSourceInput } from '@fullcalendar/core'
|
||||||
import dayGridPlugin from '@fullcalendar/daygrid'
|
import dayGridPlugin from '@fullcalendar/daygrid'
|
||||||
import interactionPlugin from '@fullcalendar/interaction'
|
import interactionPlugin from '@fullcalendar/interaction'
|
||||||
import timeGridPlugin from '@fullcalendar/timegrid'
|
import timeGridPlugin from '@fullcalendar/timegrid'
|
||||||
@@ -15,6 +15,10 @@ import { openSharedDialog } from '@/composables/useSharedDialog'
|
|||||||
|
|
||||||
const ProgressDialog = defineAsyncComponent(() => import('@/components/dialog/ProgressDialog.vue'))
|
const ProgressDialog = defineAsyncComponent(() => import('@/components/dialog/ProgressDialog.vue'))
|
||||||
|
|
||||||
|
const COLLAPSED_DAY_CARD_LIMIT = 5
|
||||||
|
const COLLAPSED_VISIBLE_CARD_LIMIT = COLLAPSED_DAY_CARD_LIMIT
|
||||||
|
const DAY_GROUP_EVENT_PREFIX = 'calendar-day-group-'
|
||||||
|
|
||||||
// 国际化
|
// 国际化
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
@@ -35,6 +39,7 @@ let progressDialogController: ReturnType<typeof openSharedDialog> | null = null
|
|||||||
type CalendarLibraryState = 'none' | 'partial' | 'complete'
|
type CalendarLibraryState = 'none' | 'partial' | 'complete'
|
||||||
|
|
||||||
interface CalendarEventInfo {
|
interface CalendarEventInfo {
|
||||||
|
id?: string
|
||||||
title: string
|
title: string
|
||||||
subtitle: string
|
subtitle: string
|
||||||
start: Date | null
|
start: Date | null
|
||||||
@@ -49,6 +54,9 @@ interface CalendarEventInfo {
|
|||||||
libraryEpisodeNumbers: number[]
|
libraryEpisodeNumbers: number[]
|
||||||
libraryState: CalendarLibraryState
|
libraryState: CalendarLibraryState
|
||||||
libraryUpdateText: string
|
libraryUpdateText: string
|
||||||
|
dateKey?: string
|
||||||
|
hiddenEventCount?: number
|
||||||
|
calendarSortIndex?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开订阅日历共享进度弹窗。
|
// 打开订阅日历共享进度弹窗。
|
||||||
@@ -85,10 +93,12 @@ const calendarOptions: Ref<CalendarOptions> = ref({
|
|||||||
center: 'title',
|
center: 'title',
|
||||||
right: 'next',
|
right: 'next',
|
||||||
},
|
},
|
||||||
// 日历页需要完整展示每天所有订阅条目,避免折叠成 "+ more" 后隐藏关键信息。
|
// 折叠逻辑由组件自行控制,点击展开时可以直接扩展当前日期格子。
|
||||||
dayMaxEvents: false,
|
dayMaxEvents: false,
|
||||||
dayMaxEventRows: false,
|
dayMaxEventRows: false,
|
||||||
eventDisplay: 'block',
|
eventDisplay: 'block',
|
||||||
|
eventOrder: 'start,calendarSortIndex,title',
|
||||||
|
eventOrderStrict: true,
|
||||||
views: {
|
views: {
|
||||||
week: {
|
week: {
|
||||||
titleFormat: { day: 'numeric' },
|
titleFormat: { day: 'numeric' },
|
||||||
@@ -97,6 +107,91 @@ const calendarOptions: Ref<CalendarOptions> = ref({
|
|||||||
events: [],
|
events: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 原始日历事件与已展开日期分离,避免依赖 FullCalendar 的弹窗式 more 链接。
|
||||||
|
const rawCalendarEvents = ref<CalendarEventInfo[]>([])
|
||||||
|
const expandedDateKeys = ref(new Set<string>())
|
||||||
|
const calendarRef = ref<InstanceType<typeof FullCalendar> | null>(null)
|
||||||
|
|
||||||
|
function getDateKey(date: Date | null) {
|
||||||
|
if (!date) return ''
|
||||||
|
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = `${date.getMonth() + 1}`.padStart(2, '0')
|
||||||
|
const day = `${date.getDate()}`.padStart(2, '0')
|
||||||
|
|
||||||
|
return `${year}-${month}-${day}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDayGroupEventId(dateKey: string) {
|
||||||
|
return `${DAY_GROUP_EVENT_PREFIX}${dateKey}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDayGroupCalendarEvent(dateKey: string, events: CalendarEventInfo[]): EventInput {
|
||||||
|
const isExpanded = expandedDateKeys.value.has(dateKey)
|
||||||
|
const visibleEvents = isExpanded ? events : events.slice(0, COLLAPSED_VISIBLE_CARD_LIMIT)
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: getDayGroupEventId(dateKey),
|
||||||
|
title: '',
|
||||||
|
start: events[0]?.start || undefined,
|
||||||
|
allDay: false,
|
||||||
|
interactive: false,
|
||||||
|
calendarSortIndex: events[0]?.calendarSortIndex ?? 0,
|
||||||
|
dateKey,
|
||||||
|
hiddenEventCount: isExpanded ? 0 : Math.max(events.length - COLLAPSED_VISIBLE_CARD_LIMIT, 0),
|
||||||
|
isDayGroup: true,
|
||||||
|
visibleEvents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeCalendarEventOrder(events: CalendarEventInfo[]) {
|
||||||
|
return events
|
||||||
|
.sort((first, second) => {
|
||||||
|
const firstTime = first.start?.getTime() ?? 0
|
||||||
|
const secondTime = second.start?.getTime() ?? 0
|
||||||
|
|
||||||
|
return firstTime - secondTime || first.title.localeCompare(second.title)
|
||||||
|
})
|
||||||
|
.map((event, index) => ({
|
||||||
|
...event,
|
||||||
|
calendarSortIndex: index,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderVisibleCalendarEvents() {
|
||||||
|
const groupedEvents = new Map<string, CalendarEventInfo[]>()
|
||||||
|
|
||||||
|
rawCalendarEvents.value.forEach(event => {
|
||||||
|
const dateKey = getDateKey(event.start)
|
||||||
|
if (!dateKey) return
|
||||||
|
|
||||||
|
groupedEvents.set(dateKey, [...(groupedEvents.get(dateKey) || []), event])
|
||||||
|
})
|
||||||
|
|
||||||
|
calendarOptions.value.events = Array.from(groupedEvents.entries()).map(([dateKey, events]) =>
|
||||||
|
createDayGroupCalendarEvent(dateKey, events),
|
||||||
|
) as EventSourceInput
|
||||||
|
}
|
||||||
|
|
||||||
|
function expandCalendarDay(dateKey: string) {
|
||||||
|
const currentScrollY = window.scrollY
|
||||||
|
const events = rawCalendarEvents.value.filter(event => getDateKey(event.start) === dateKey)
|
||||||
|
const calendarApi = calendarRef.value?.getApi()
|
||||||
|
|
||||||
|
expandedDateKeys.value = new Set(expandedDateKeys.value).add(dateKey)
|
||||||
|
|
||||||
|
// 只更新当天这个聚合事件的内容,避免重置整个 FullCalendar 导致页面回到顶部。
|
||||||
|
if (calendarApi) {
|
||||||
|
const event = calendarApi.getEventById(getDayGroupEventId(dateKey))
|
||||||
|
event?.setExtendedProp('visibleEvents', events)
|
||||||
|
event?.setExtendedProp('hiddenEventCount', 0)
|
||||||
|
|
||||||
|
requestAnimationFrame(() => window.scrollTo({ top: currentScrollY, left: window.scrollX }))
|
||||||
|
} else {
|
||||||
|
renderVisibleCalendarEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function clampEpisodeCount(value: number, total: number) {
|
function clampEpisodeCount(value: number, total: number) {
|
||||||
return Math.min(Math.max(value, 0), total)
|
return Math.min(Math.max(value, 0), total)
|
||||||
}
|
}
|
||||||
@@ -183,15 +278,20 @@ function buildCalendarEventInfo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCalendarEventTooltip(event: any) {
|
function getExpandCalendarEventLabel(event: any) {
|
||||||
const props = event.extendedProps as CalendarEventInfo
|
const props = event.extendedProps as CalendarEventInfo
|
||||||
|
|
||||||
|
return t('calendar.expandDayEvents', { count: props.hiddenEventCount || 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCalendarEventInfoTooltip(event: CalendarEventInfo) {
|
||||||
const parts = [event.title]
|
const parts = [event.title]
|
||||||
|
|
||||||
if (props.subtitle) parts.push(t('calendar.episode', { number: props.subtitle }))
|
if (event.subtitle) parts.push(t('calendar.episode', { number: event.subtitle }))
|
||||||
if (props.totalEpisode) {
|
if (event.totalEpisode) {
|
||||||
parts.push(t('calendar.libraryProgress', { completed: props.libraryEpisode, total: props.totalEpisode }))
|
parts.push(t('calendar.libraryProgress', { completed: event.libraryEpisode, total: event.totalEpisode }))
|
||||||
}
|
}
|
||||||
if (props.libraryUpdateText) parts.push(t('calendar.libraryUpdatedAt', { time: props.libraryUpdateText }))
|
if (event.libraryUpdateText) parts.push(t('calendar.libraryUpdatedAt', { time: event.libraryUpdateText }))
|
||||||
|
|
||||||
return parts.filter(Boolean).join(' · ')
|
return parts.filter(Boolean).join(' · ')
|
||||||
}
|
}
|
||||||
@@ -273,7 +373,8 @@ async function getSubscribes() {
|
|||||||
loading.value = false
|
loading.value = false
|
||||||
const subEvents = await Promise.allSettled(subscribes.map(async sub => eventsHander(sub)))
|
const subEvents = await Promise.allSettled(subscribes.map(async sub => eventsHander(sub)))
|
||||||
const succEvents = subEvents.filter(result => result.status === 'fulfilled').map(result => result.value)
|
const succEvents = subEvents.filter(result => result.status === 'fulfilled').map(result => result.value)
|
||||||
calendarOptions.value.events = succEvents.flat().filter(event => event.start) as EventSourceInput
|
rawCalendarEvents.value = normalizeCalendarEventOrder(succEvents.flat().filter(event => event.start))
|
||||||
|
renderVisibleCalendarEvents()
|
||||||
isLoaded.value = true
|
isLoaded.value = true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@@ -295,19 +396,24 @@ onActivated(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FullCalendar :options="calendarOptions">
|
<FullCalendar ref="calendarRef" :options="calendarOptions">
|
||||||
<template #eventContent="arg">
|
<template #eventContent="arg">
|
||||||
<div v-if="display.lgAndUp.value">
|
<div
|
||||||
|
v-if="arg.event.extendedProps.isDayGroup"
|
||||||
|
class="calendar-day-events"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
|
v-for="calendarEvent in arg.event.extendedProps.visibleEvents"
|
||||||
|
:key="`${calendarEvent.title}-${calendarEvent.subtitle}-${calendarEvent.calendarSortIndex}`"
|
||||||
class="calendar-event-card"
|
class="calendar-event-card"
|
||||||
:class="`calendar-event-card--${arg.event.extendedProps.libraryState}`"
|
:class="`calendar-event-card--${calendarEvent.libraryState}`"
|
||||||
:title="getCalendarEventTooltip(arg.event)"
|
:title="getCalendarEventInfoTooltip(calendarEvent)"
|
||||||
>
|
>
|
||||||
<div class="calendar-event-poster">
|
<div v-if="display.lgAndUp.value" class="calendar-event-poster">
|
||||||
<VImg
|
<VImg
|
||||||
height="74"
|
height="74"
|
||||||
width="50"
|
width="50"
|
||||||
:src="arg.event.extendedProps.posterPath"
|
:src="calendarEvent.posterPath"
|
||||||
aspect-ratio="2/3"
|
aspect-ratio="2/3"
|
||||||
class="calendar-event-image object-cover"
|
class="calendar-event-image object-cover"
|
||||||
cover
|
cover
|
||||||
@@ -319,70 +425,82 @@ onActivated(() => {
|
|||||||
</template>
|
</template>
|
||||||
</VImg>
|
</VImg>
|
||||||
<span
|
<span
|
||||||
v-if="arg.event.extendedProps.libraryState === 'complete'"
|
v-if="calendarEvent.libraryState === 'complete'"
|
||||||
class="calendar-library-check"
|
class="calendar-library-check"
|
||||||
>
|
>
|
||||||
<VIcon icon="mdi-check" size="12" />
|
<VIcon icon="mdi-check" size="12" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="calendar-event-content">
|
<VImg
|
||||||
|
v-else
|
||||||
|
:src="calendarEvent.posterPath"
|
||||||
|
aspect-ratio="2/3"
|
||||||
|
class="calendar-mobile-image object-cover ring-gray-500"
|
||||||
|
cover
|
||||||
|
:title="getCalendarEventInfoTooltip(calendarEvent)"
|
||||||
|
>
|
||||||
|
<template #placeholder>
|
||||||
|
<div class="w-full h-full">
|
||||||
|
<VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<span
|
||||||
|
v-if="calendarEvent.libraryState === 'complete'"
|
||||||
|
class="calendar-library-check calendar-library-check--mobile"
|
||||||
|
>
|
||||||
|
<VIcon icon="mdi-check" size="11" />
|
||||||
|
</span>
|
||||||
|
<span v-if="calendarEvent.subtitle" class="calendar-mobile-episode">
|
||||||
|
{{ calendarEvent.subtitle }}
|
||||||
|
</span>
|
||||||
|
</VImg>
|
||||||
|
|
||||||
|
<div v-if="display.lgAndUp.value" class="calendar-event-content">
|
||||||
<div class="calendar-event-title">
|
<div class="calendar-event-title">
|
||||||
{{ arg.event.title }}
|
{{ calendarEvent.title }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="arg.event.extendedProps.subtitle" class="calendar-event-episode">
|
<div v-if="calendarEvent.subtitle" class="calendar-event-episode">
|
||||||
<VIcon icon="mdi-calendar-blank-outline" size="13" />
|
<VIcon icon="mdi-calendar-blank-outline" size="13" />
|
||||||
{{ t('calendar.episode', { number: arg.event.extendedProps.subtitle }) }}
|
{{ t('calendar.episode', { number: calendarEvent.subtitle }) }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="arg.event.extendedProps.totalEpisode" class="calendar-event-library-row">
|
<div v-if="calendarEvent.totalEpisode" class="calendar-event-library-row">
|
||||||
<span
|
<span
|
||||||
v-if="arg.event.extendedProps.libraryState !== 'complete'"
|
v-if="calendarEvent.libraryState !== 'complete'"
|
||||||
class="calendar-event-status"
|
class="calendar-event-status"
|
||||||
:class="`calendar-event-status--${arg.event.extendedProps.libraryState}`"
|
:class="`calendar-event-status--${calendarEvent.libraryState}`"
|
||||||
>
|
>
|
||||||
<VIcon :icon="getLibraryStateIcon(arg.event.extendedProps.libraryState)" size="13" />
|
<VIcon :icon="getLibraryStateIcon(calendarEvent.libraryState)" size="13" />
|
||||||
{{ getLibraryStateText(arg.event.extendedProps.libraryState) }}
|
{{ getLibraryStateText(calendarEvent.libraryState) }}
|
||||||
</span>
|
</span>
|
||||||
<span class="calendar-event-progress">
|
<span class="calendar-event-progress">
|
||||||
<VIcon icon="mdi-library" size="13" />
|
<VIcon icon="mdi-library" size="13" />
|
||||||
{{
|
{{
|
||||||
t('calendar.libraryProgress', {
|
t('calendar.libraryProgress', {
|
||||||
completed: arg.event.extendedProps.libraryEpisode,
|
completed: calendarEvent.libraryEpisode,
|
||||||
total: arg.event.extendedProps.totalEpisode,
|
total: calendarEvent.totalEpisode,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="arg.event.extendedProps.libraryUpdateText" class="calendar-event-time">
|
<div v-if="calendarEvent.libraryUpdateText" class="calendar-event-time">
|
||||||
<VIcon icon="mdi-clock-outline" size="13" />
|
<VIcon icon="mdi-clock-outline" size="13" />
|
||||||
{{ t('calendar.libraryUpdatedAtShort', { time: arg.event.extendedProps.libraryUpdateText }) }}
|
{{ t('calendar.libraryUpdatedAtShort', { time: calendarEvent.libraryUpdateText }) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div v-else>
|
<button
|
||||||
<VImg
|
v-if="arg.event.extendedProps.hiddenEventCount"
|
||||||
:src="arg.event.extendedProps.posterPath"
|
type="button"
|
||||||
aspect-ratio="2/3"
|
class="calendar-expand-card"
|
||||||
class="calendar-mobile-image object-cover ring-gray-500"
|
:title="getExpandCalendarEventLabel(arg.event)"
|
||||||
cover
|
:aria-label="getExpandCalendarEventLabel(arg.event)"
|
||||||
:title="getCalendarEventTooltip(arg.event)"
|
@click.stop.prevent="expandCalendarDay(arg.event.extendedProps.dateKey)"
|
||||||
>
|
>
|
||||||
<template #placeholder>
|
<VIcon icon="mdi-unfold-more-horizontal" size="18" />
|
||||||
<div class="w-full h-full">
|
<span class="calendar-expand-count">+{{ arg.event.extendedProps.hiddenEventCount }}</span>
|
||||||
<VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" />
|
</button>
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<span
|
|
||||||
v-if="arg.event.extendedProps.libraryState === 'complete'"
|
|
||||||
class="calendar-library-check calendar-library-check--mobile"
|
|
||||||
>
|
|
||||||
<VIcon icon="mdi-check" size="11" />
|
|
||||||
</span>
|
|
||||||
<span v-if="arg.event.extendedProps.subtitle" class="calendar-mobile-episode">
|
|
||||||
{{ arg.event.extendedProps.subtitle }}
|
|
||||||
</span>
|
|
||||||
</VImg>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</FullCalendar>
|
</FullCalendar>
|
||||||
@@ -679,6 +797,38 @@ onActivated(() => {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.calendar-day-events {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.3rem;
|
||||||
|
inline-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-expand-card {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.35rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-block-size: 2.1rem;
|
||||||
|
border: 1px dashed rgba(var(--v-theme-primary), 0.44);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(var(--v-theme-primary), 0.08);
|
||||||
|
color: rgb(var(--v-theme-primary));
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 700;
|
||||||
|
inline-size: 100%;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-expand-card:hover {
|
||||||
|
background: rgba(var(--v-theme-primary), 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-expand-count {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.calendar-event-poster {
|
.calendar-event-poster {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 0 0 56px;
|
flex: 0 0 56px;
|
||||||
@@ -828,10 +978,27 @@ onActivated(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (width <= 1279px) {
|
@media (width <= 1279px) {
|
||||||
|
.calendar-day-events {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event-card,
|
||||||
.fc-daygrid-event-harness {
|
.fc-daygrid-event-harness {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.calendar-expand-card {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.12rem;
|
||||||
|
min-block-size: 0;
|
||||||
|
block-size: clamp(60px, 8.7vw, 96px);
|
||||||
|
inline-size: clamp(40px, 5.8vw, 64px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-expand-count {
|
||||||
|
font-size: 0.68rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user