replace el-popover el-card

This commit is contained in:
pa
2026-01-13 14:28:25 +09:00
committed by Natsumi
parent 911969289e
commit 77a838e22d
19 changed files with 254 additions and 76 deletions

View File

@@ -624,9 +624,6 @@ html.dark .x-friend-item > .detail > .extra,
word-break: break-word;
}
.el-card.x-image-selected {
border-color: var(--el-color-primary) !important;
}
.el-popper.x-quick-search .el-select-dropdown__item {
width: 100%;

View File

@@ -1076,17 +1076,16 @@
max-height: 600px;
overflow-y: auto;
">
<el-card
<Card
v-for="image in groupDialog.galleries[gallery.id]"
:key="image.id"
:body-style="{ padding: '0' }"
shadow="hover">
class="p-0 overflow-hidden transition-shadow hover:shadow-md">
<img
:src="image.imageUrl"
:class="['x-link', 'x-popover-image']"
@click="showFullscreenImageDialog(image.imageUrl)"
loading="lazy" />
</el-card>
</Card>
</div>
</el-tab-pane>
</template>
@@ -1147,6 +1146,7 @@
View,
Warning
} from '@element-plus/icons-vue';
import { Card } from '@/components/ui/card';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { computed, nextTick, reactive, ref, watch } from 'vue';
import { Button } from '@/components/ui/button';

View File

@@ -39,14 +39,12 @@
max-height: 600px;
overflow-y: auto;
">
<el-card
<div
v-for="image in emojiTable"
:key="image.id"
:body-style="{ padding: '0' }"
:class="image.id === fileId ? 'x-image-selected' : ''"
style="cursor: pointer"
@click="fileId = image.id"
shadow="hover">
style="cursor: pointer; border: 1px solid transparent; border-radius: 8px"
@click="fileId = image.id">
<div
v-if="
image.versions &&
@@ -57,7 +55,7 @@
style="padding: 8px">
<Emoji :imageUrl="image.versions[image.versions.length - 1].file.url" :size="100"></Emoji>
</div>
</el-card>
</div>
</div>
<template #footer>

View File

@@ -0,0 +1,15 @@
<script setup>
import { cn } from '@/lib/utils';
const props = defineProps({
class: { type: null, required: false }
});
</script>
<template>
<div
data-slot="card"
:class="cn('bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm', props.class)">
<slot />
</div>
</template>

View File

@@ -0,0 +1,15 @@
<script setup>
import { cn } from '@/lib/utils';
const props = defineProps({
class: { type: null, required: false }
});
</script>
<template>
<div
data-slot="card-action"
:class="cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', props.class)">
<slot />
</div>
</template>

View File

@@ -0,0 +1,13 @@
<script setup>
import { cn } from '@/lib/utils';
const props = defineProps({
class: { type: null, required: false }
});
</script>
<template>
<div data-slot="card-content" :class="cn('px-6', props.class)">
<slot />
</div>
</template>

View File

@@ -0,0 +1,13 @@
<script setup>
import { cn } from '@/lib/utils';
const props = defineProps({
class: { type: null, required: false }
});
</script>
<template>
<p data-slot="card-description" :class="cn('text-muted-foreground text-sm', props.class)">
<slot />
</p>
</template>

View File

@@ -0,0 +1,13 @@
<script setup>
import { cn } from '@/lib/utils';
const props = defineProps({
class: { type: null, required: false }
});
</script>
<template>
<div data-slot="card-footer" :class="cn('flex items-center px-6 [.border-t]:pt-6', props.class)">
<slot />
</div>
</template>

View File

@@ -0,0 +1,20 @@
<script setup>
import { cn } from '@/lib/utils';
const props = defineProps({
class: { type: null, required: false }
});
</script>
<template>
<div
data-slot="card-header"
:class="
cn(
'@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
props.class
)
">
<slot />
</div>
</template>

View File

@@ -0,0 +1,13 @@
<script setup>
import { cn } from '@/lib/utils';
const props = defineProps({
class: { type: null, required: false }
});
</script>
<template>
<h3 data-slot="card-title" :class="cn('leading-none font-semibold', props.class)">
<slot />
</h3>
</template>

View File

@@ -0,0 +1,7 @@
export { default as Card } from "./Card.vue";
export { default as CardAction } from "./CardAction.vue";
export { default as CardContent } from "./CardContent.vue";
export { default as CardDescription } from "./CardDescription.vue";
export { default as CardFooter } from "./CardFooter.vue";
export { default as CardHeader } from "./CardHeader.vue";
export { default as CardTitle } from "./CardTitle.vue";

View File

@@ -0,0 +1,19 @@
<script setup>
import { HoverCardRoot, useForwardPropsEmits } from 'reka-ui';
const props = defineProps({
defaultOpen: { type: Boolean, required: false },
open: { type: Boolean, required: false },
openDelay: { type: Number, required: false },
closeDelay: { type: Number, required: false }
});
const emits = defineEmits(['update:open']);
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<HoverCardRoot v-slot="slotProps" data-slot="hover-card" v-bind="forwarded">
<slot v-bind="slotProps" />
</HoverCardRoot>
</template>

View File

@@ -0,0 +1,53 @@
<script setup>
import { HoverCardContent, HoverCardPortal, useForwardProps } from 'reka-ui';
import { cn } from '@/lib/utils';
import { reactiveOmit } from '@vueuse/core';
defineOptions({
inheritAttrs: false
});
const props = defineProps({
forceMount: { type: Boolean, required: false },
side: { type: null, required: false },
sideOffset: { type: Number, required: false, default: 4 },
sideFlip: { type: Boolean, required: false },
align: { type: null, required: false },
alignOffset: { type: Number, required: false },
alignFlip: { type: Boolean, required: false },
avoidCollisions: { type: Boolean, required: false },
collisionBoundary: { type: null, required: false },
collisionPadding: { type: [Number, Object], required: false },
arrowPadding: { type: Number, required: false },
sticky: { type: String, required: false },
hideWhenDetached: { type: Boolean, required: false },
positionStrategy: { type: String, required: false },
updatePositionStrategy: { type: String, required: false },
disableUpdateOnLayoutShift: { type: Boolean, required: false },
prioritizePosition: { type: Boolean, required: false },
reference: { type: null, required: false },
asChild: { type: Boolean, required: false },
as: { type: null, required: false },
class: { type: null, required: false }
});
const delegatedProps = reactiveOmit(props, 'class');
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<HoverCardPortal>
<HoverCardContent
data-slot="hover-card-content"
v-bind="{ ...$attrs, ...forwardedProps }"
:class="
cn(
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 rounded-md border p-4 shadow-md outline-hidden',
props.class
)
">
<slot />
</HoverCardContent>
</HoverCardPortal>
</template>

View File

@@ -0,0 +1,15 @@
<script setup>
import { HoverCardTrigger } from 'reka-ui';
const props = defineProps({
reference: { type: null, required: false },
asChild: { type: Boolean, required: false },
as: { type: null, required: false }
});
</script>
<template>
<HoverCardTrigger data-slot="hover-card-trigger" v-bind="props">
<slot />
</HoverCardTrigger>
</template>

View File

@@ -0,0 +1,3 @@
export { default as HoverCard } from './HoverCard.vue';
export { default as HoverCardContent } from './HoverCardContent.vue';
export { default as HoverCardTrigger } from './HoverCardTrigger.vue';

View File

@@ -3,21 +3,22 @@
<div class="options-container instance-activity" style="margin-top: 0">
<div>
<span>{{ t('view.charts.instance_activity.header') }}</span>
<el-popover placement="bottom-start" trigger="hover" :width="300">
<div class="tips-popover">
<div>{{ t('view.charts.instance_activity.tips.online_time') }}</div>
<div>{{ t('view.charts.instance_activity.tips.click_Y_axis') }}</div>
<div>{{ t('view.charts.instance_activity.tips.click_instance_name') }}</div>
<div>
<el-icon><WarningFilled /></el-icon
><i>{{ t('view.charts.instance_activity.tips.accuracy_notice') }}</i>
</div>
</div>
<template #reference>
<HoverCard>
<HoverCardTrigger as-child>
<el-icon style="margin-left: 5px; font-size: 12px; opacity: 0.7"><InfoFilled /></el-icon>
</template>
</el-popover>
</HoverCardTrigger>
<HoverCardContent side="bottom" align="start" class="w-[300px]">
<div class="tips-popover">
<div>{{ t('view.charts.instance_activity.tips.online_time') }}</div>
<div>{{ t('view.charts.instance_activity.tips.click_Y_axis') }}</div>
<div>{{ t('view.charts.instance_activity.tips.click_instance_name') }}</div>
<div>
<el-icon><WarningFilled /></el-icon
><i>{{ t('view.charts.instance_activity.tips.accuracy_notice') }}</i>
</div>
</div>
</HoverCardContent>
</HoverCard>
</div>
<div>
@@ -145,6 +146,7 @@
<script setup>
import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { ArrowLeft, ArrowRight, InfoFilled, WarningFilled } from '@element-plus/icons-vue';
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card';
import { RefreshCcw, Settings } from 'lucide-vue-next';
import { Button } from '@/components/ui/button';
import { ButtonGroup } from '@/components/ui/button-group';

View File

@@ -1,10 +1,5 @@
<template>
<el-card
class="friend-card"
shadow="never"
:body-style="{ padding: `${16 * cardScale * cardSpacing}px` }"
:style="cardStyle"
@click="showUserDialog(friend.id)">
<Card class="friend-card p-0 gap-0" :style="cardStyle" @click="showUserDialog(friend.id)">
<div class="friend-card__header">
<div class="friend-card__avatar-wrapper">
<el-avatar :size="avatarSize" :src="userImage(props.friend.ref, true)" class="friend-card__avatar">
@@ -27,11 +22,12 @@
link />
</div>
</div>
</el-card>
</Card>
</template>
<script setup>
import { computed } from 'vue';
import { Card } from '@/components/ui/card';
import { userImage, userStatusClass } from '../../../shared/utils';
import { useUserStore } from '../../../stores';
@@ -62,7 +58,8 @@
const cardStyle = computed(() => ({
'--card-scale': props.cardScale,
'--card-spacing': props.cardSpacing,
cursor: 'pointer'
cursor: 'pointer',
padding: `${16 * props.cardScale * props.cardSpacing}px`
}));
const avatarFallback = computed(() => props.friend.name.charAt(0) ?? '?');

View File

@@ -12,7 +12,7 @@
<span class="category-title">{{ t('view.tools.group.header') }}</span>
</div>
<div class="tools-grid" v-show="!categoryCollapsed['group']">
<el-card :body-style="{ padding: '0px' }" class="tool-card">
<Card class="tool-card p-0 gap-0">
<div class="tool-content" @click="showGroupCalendarDialog">
<div class="tool-icon">
<i class="ri-calendar-event-line"></i>
@@ -22,7 +22,7 @@
<div class="tool-description">{{ t('view.tools.group.calendar_description') }}</div>
</div>
</div>
</el-card>
</Card>
</div>
</div>
@@ -34,7 +34,7 @@
<span class="category-title">{{ t('view.tools.pictures.header') }}</span>
</div>
<div class="tools-grid" v-show="!categoryCollapsed['image']">
<el-card :body-style="{ padding: '0px' }" class="tool-card">
<Card class="tool-card p-0 gap-0">
<div class="tool-content" @click="showScreenshotMetadataPage">
<div class="tool-icon">
<i class="ri-screenshot-2-line"></i>
@@ -46,8 +46,8 @@
</div>
</div>
</div>
</el-card>
<el-card :body-style="{ padding: '0px' }" class="tool-card">
</Card>
<Card class="tool-card p-0 gap-0">
<div class="tool-content" @click="showGalleryPage">
<div class="tool-icon">
<i class="ri-multi-image-line"></i>
@@ -59,8 +59,8 @@
</div>
</div>
</div>
</el-card>
<el-card :body-style="{ padding: '0px' }" class="tool-card">
</Card>
<Card class="tool-card p-0 gap-0">
<div class="tool-content" @click="openVrcPhotosFolder">
<div class="tool-icon">
<i class="ri-folder-image-line"></i>
@@ -72,8 +72,8 @@
</div>
</div>
</div>
</el-card>
<el-card :body-style="{ padding: '0px' }" class="tool-card">
</Card>
<Card class="tool-card p-0 gap-0">
<div class="tool-content" @click="openVrcScreenshotsFolder">
<div class="tool-icon">
<i class="ri-folder-image-line"></i>
@@ -87,7 +87,7 @@
</div>
</div>
</div>
</el-card>
</Card>
</div>
</div>
@@ -100,7 +100,7 @@
</div>
<div class="tools-grid" v-show="!categoryCollapsed['user']">
<el-card :body-style="{ padding: '0px' }" class="tool-card">
<Card class="tool-card p-0 gap-0">
<div class="tool-content" @click="showExportDiscordNamesDialog">
<div class="tool-icon">
<i class="ri-discord-line"></i>
@@ -112,8 +112,8 @@
</div>
</div>
</div>
</el-card>
<el-card :body-style="{ padding: '0px' }" class="tool-card">
</Card>
<Card class="tool-card p-0 gap-0">
<div class="tool-content" @click="showNoteExportDialog">
<div class="tool-icon">
<i class="ri-user-shared-line"></i>
@@ -125,9 +125,9 @@
</div>
</div>
</div>
</el-card>
</Card>
<el-card :body-style="{ padding: '0px' }" class="tool-card">
<Card class="tool-card p-0 gap-0">
<div class="tool-content" @click="showExportFriendsListDialog">
<div class="tool-icon">
<i class="ri-user-shared-line"></i>
@@ -139,8 +139,8 @@
</div>
</div>
</div>
</el-card>
<el-card :body-style="{ padding: '0px' }" class="tool-card">
</Card>
<Card class="tool-card p-0 gap-0">
<div class="tool-content" @click="showExportAvatarsListDialog">
<div class="tool-icon">
<i class="ri-user-shared-line"></i>
@@ -152,7 +152,7 @@
</div>
</div>
</div>
</el-card>
</Card>
</div>
</div>
@@ -164,7 +164,7 @@
<span class="category-title">{{ t('view.tools.other.header') }}</span>
</div>
<div class="tools-grid" v-show="!categoryCollapsed['other']">
<el-card :body-style="{ padding: '0px' }" class="tool-card">
<Card class="tool-card p-0 gap-0">
<div class="tool-content" @click="showEditInviteMessageDialog">
<div class="tool-icon">
<i class="ri-edit-box-line"></i>
@@ -176,7 +176,7 @@
</div>
</div>
</div>
</el-card>
</Card>
</div>
</div>
</div>
@@ -206,6 +206,7 @@
import { computed, defineAsyncComponent, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ArrowRight } from '@element-plus/icons-vue';
import { Card } from '@/components/ui/card';
import { storeToRefs } from 'pinia';
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
@@ -347,16 +348,13 @@
overflow: visible;
border-radius: 8px;
cursor: pointer;
width: 100%;
&:hover {
transform: translateY(-2px);
box-shadow: var(--el-box-shadow-light);
}
:deep(.el-card__body) {
overflow: visible;
}
.tool-content {
display: flex;
align-items: center;
@@ -398,12 +396,6 @@
}
}
:deep(.el-card) {
border-radius: 8px;
width: 100%;
overflow: visible;
}
.is-rotated {
transform: rotate(90deg);
}

View File

@@ -1,5 +1,5 @@
<template>
<el-card :body-style="{ padding: '0px' }" class="event-card" :class="cardClass">
<Card class="event-card p-0 gap-0" :class="cardClass">
<img :src="bannerUrl" @click="showFullscreenImageDialog(bannerUrl)" class="banner" />
<div class="event-content">
<div class="event-title">
@@ -69,12 +69,13 @@
<el-icon><StarFilled /></el-icon>
</div>
</div>
</el-card>
</Card>
</template>
<script setup>
import { Calendar, Download, Share, Star, StarFilled } from '@element-plus/icons-vue';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { computed } from 'vue';
import { toast } from 'vue-sonner';
import { useI18n } from 'vue-i18n';
@@ -205,6 +206,7 @@
position: relative;
overflow: visible;
border-radius: 8px;
width: 100%;
}
.event-card:hover {
@@ -226,10 +228,6 @@
max-width: 320px;
}
.event-card :deep(.el-card__body) {
overflow: visible;
}
.event-card .banner {
cursor: pointer;
width: 100%;
@@ -351,9 +349,4 @@
color: var(--el-color-primary);
}
:deep(.el-card) {
border-radius: 8px;
width: 100%;
overflow: visible;
}
</style>