replace element plus components

This commit is contained in:
pa
2026-01-15 22:38:09 +09:00
committed by Natsumi
parent bdc1d3a347
commit c430ce1b63
46 changed files with 2143 additions and 1752 deletions

View File

@@ -42,11 +42,7 @@
const timeZone = getLocalTimeZone();
// JSDoc casts: this project can end up with nominal-type mismatches for DateValue
// due to duplicate @internationalized/date copies in tooling.
/** @type {import('vue').Ref<any>} */
const internalValue = ref(fromDate(props.modelValue ?? new Date(), timeZone));
/** @type {import('vue').Ref<any>} */
const placeholder = ref(fromDate(props.modelValue ?? new Date(), timeZone));
watch(
@@ -147,6 +143,10 @@
:class="hasFollowingFor(weekDate) ? 'has-following' : 'no-following'">
{{ eventCountFor(weekDate) }}
</div>
<!-- <div
v-if="eventCountFor(weekDate) > 0"
class="calendar-event-dot"
aria-hidden="true" /> -->
</div>
</div>
</CalendarCellTrigger>
@@ -165,7 +165,6 @@
width: 100%;
display: flex;
align-items: flex-start;
justify-content: center;
}
.date {
@@ -193,21 +192,20 @@
.calendar-event-badge {
position: absolute;
top: 2px;
right: 2px;
min-width: 18px;
height: 18px;
top: -4px;
right: -4px;
min-width: 14px;
height: 14px;
border-radius: 9px;
color: var(--el-color-white, #fff);
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: bold;
font-size: 10px;
box-shadow: var(--el-box-shadow-lighter);
z-index: 10;
padding: 0 5px;
line-height: 18px;
line-height: 14px;
}
.calendar-event-badge.has-following {
@@ -217,4 +215,18 @@
.calendar-event-badge.no-following {
background-color: var(--group-calendar-badge-normal, var(--el-color-primary));
}
.calendar-event-dot {
position: absolute;
left: 50%;
bottom: 4px;
transform: translateX(-50%);
width: 6px;
height: 6px;
border-radius: 9999px;
background-color: var(--group-calendar-event-dot, #ef4444);
box-shadow: var(--el-box-shadow-lighter);
z-index: 5;
pointer-events: none;
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<Dialog :open="visible" @update:open="(open) => (open ? null : closeDialog())">
<DialogContent class="x-dialog w-[90vw] max-w-[90vw] sm:max-w-[70vw] h-[60vh] overflow-hidden">
<DialogContent class="x-dialog sm:max-w-[50vw] h-[60vh] overflow-hidden">
<DialogHeader>
<div class="dialog-title-container">
<DialogTitle>{{ t('dialog.group_calendar.header') }}</DialogTitle>
@@ -18,82 +18,77 @@
</div>
</DialogHeader>
<div class="top-content">
<transition name="el-fade-in-linear" mode="out-in">
<div v-if="viewMode === 'timeline'" key="timeline" class="timeline-view">
<div class="timeline-container">
<el-timeline v-if="groupedTimelineEvents.length">
<el-timeline-item
v-for="(timeGroup, key) of groupedTimelineEvents"
:key="key"
:timestamp="
dayjs(timeGroup.startsAt).format('MM-DD ddd') + ' ' + timeGroup.startTime
"
placement="top">
<div class="time-group-container">
<GroupCalendarEventCard
v-for="value in timeGroup.events"
:key="value.id"
:event="value"
mode="timeline"
:is-following="isEventFollowing(value.id)"
:card-class="{ 'grouped-card': timeGroup.events.length > 1 }"
@update-following-calendar-data="updateFollowingCalendarData"
@click-action="showGroupDialog(value.ownerId)" />
</div>
</el-timeline-item>
</el-timeline>
<div v-else>{{ t('dialog.group_calendar.no_events') }}</div>
</div>
<div class="calendar-container">
<GroupCalendarMonth
v-model="selectedDay"
:is-loading="isLoading"
:events-by-date="filteredCalendar"
:following-by-date="followingCalendarDate" />
</div>
</div>
<div v-else key="grid" class="grid-view">
<div class="search-container">
<InputGroupSearch
v-model="searchQuery"
size="sm"
:placeholder="t('dialog.group_calendar.search_placeholder')"
class="search-input" />
</div>
<div class="groups-grid" v-loading="isLoading">
<div v-if="filteredGroupEvents.length" class="groups-container">
<div v-for="group in filteredGroupEvents" :key="group.groupId" class="group-row">
<div class="group-header" @click="toggleGroup(group.groupId)">
<ArrowRight
class="rotation-transition"
:class="{ rotate: !groupCollapsed[group.groupId] }" />
{{ group.groupName }}
</div>
<div class="events-row" v-show="!groupCollapsed[group.groupId]">
<GroupCalendarEventCard
v-for="event in group.events"
:key="event.id"
:event="event"
mode="grid"
:is-following="isEventFollowing(event.id)"
@update-following-calendar-data="updateFollowingCalendarData"
@click-action="showGroupDialog(event.ownerId)"
card-class="grid-card" />
</div>
<div v-if="viewMode === 'timeline'" key="timeline" class="timeline-view">
<div class="timeline-container">
<div v-if="groupedTimelineEvents.length" class="timeline-list">
<div v-for="(timeGroup, key) of groupedTimelineEvents" :key="key" class="timeline-group">
<div class="timeline-timestamp">
{{ dayjs(timeGroup.startsAt).format('MM-DD ddd') }} {{ timeGroup.startTime }}
</div>
<div class="time-group-container">
<GroupCalendarEventCard
v-for="value in timeGroup.events"
:key="value.id"
:event="value"
mode="timeline"
:is-following="isEventFollowing(value.id)"
:card-class="{ 'grouped-card': timeGroup.events.length > 1 }"
@update-following-calendar-data="updateFollowingCalendarData"
@click-action="showGroupDialog(value.ownerId)" />
</div>
</div>
<div v-else class="no-events">
{{
searchQuery
? t('dialog.group_calendar.search_no_matching')
: t('dialog.group_calendar.search_no_this_month')
}}
</div>
<div v-else class="timeline-empty">{{ t('dialog.group_calendar.no_events') }}</div>
</div>
<div class="calendar-container">
<GroupCalendarMonth
v-model="selectedDay"
:is-loading="isLoading"
:events-by-date="filteredCalendar"
:following-by-date="followingCalendarDate" />
</div>
</div>
<div v-else key="grid" class="grid-view">
<div class="search-container">
<InputGroupSearch
v-model="searchQuery"
size="sm"
:placeholder="t('dialog.group_calendar.search_placeholder')"
class="search-input" />
</div>
<div class="groups-grid" v-loading="isLoading">
<div v-if="filteredGroupEvents.length" class="groups-container">
<div v-for="group in filteredGroupEvents" :key="group.groupId" class="group-row">
<div class="group-header" @click="toggleGroup(group.groupId)">
<ArrowRight
class="rotation-transition"
:class="{ rotate: !groupCollapsed[group.groupId] }" />
{{ group.groupName }}
</div>
<div class="events-row" v-show="!groupCollapsed[group.groupId]">
<GroupCalendarEventCard
v-for="event in group.events"
:key="event.id"
:event="event"
mode="grid"
:is-following="isEventFollowing(event.id)"
@update-following-calendar-data="updateFollowingCalendarData"
@click-action="showGroupDialog(event.ownerId)"
card-class="grid-card" />
</div>
</div>
</div>
<div v-else class="no-events">
{{
searchQuery
? t('dialog.group_calendar.search_no_matching')
: t('dialog.group_calendar.search_no_this_month')
}}
</div>
</div>
</transition>
</div>
</div>
</DialogContent>
</Dialog>
@@ -334,7 +329,8 @@
.sort((a, b) => dayjs(a.startsAt).diff(dayjs(b.startsAt)));
});
const formatDateKey = (date) => formatDateFilter(date, 'date');
// Use a stable key for calendar maps (independent of locale/appearance date formatting).
const formatDateKey = (date) => dayjs(date).format('YYYY-MM-DD');
function getGroupNameFromCache(groupId) {
if (!groupNamesCache.has(groupId)) {
@@ -462,18 +458,36 @@
overflow: hidden;
.timeline-view {
.timeline-container {
:deep(.el-timeline) {
width: 100%;
min-width: 200px;
padding-left: 4px;
padding-right: 16px;
margin-left: 10px;
margin-right: 6px;
overflow: auto;
.timeline-list {
display: flex;
flex-direction: column;
gap: 14px;
}
.timeline-group {
padding: 0 20px 6px 10px;
}
.timeline-timestamp {
font-size: 13px;
font-weight: 600;
color: var(--el-text-color-secondary);
margin-bottom: 8px;
}
.timeline-empty {
height: 100%;
min-width: 200px;
padding-left: 4px;
padding-right: 16px;
margin-left: 10px;
margin-right: 6px;
overflow: auto;
.el-timeline-item {
padding: 0 20px 20px 10px;
}
display: flex;
align-items: center;
justify-content: center;
color: var(--el-text-color-secondary);
}
.time-group-container {
display: flex;
@@ -571,7 +585,6 @@
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.calendar-container {
width: 609px;