feat(components): 新增 Drawer 组件并优化 Playground 页面
- 新增 Drawer 组件用于创建可滑动的抽屉式界面 -优化 Playground 页面布局和样式,增加 logo 和 frosted glass 效果 - 添加统计卡片组件和动画效果,提升用户体验 - 新增数据项目图标组件
This commit is contained in:
@@ -4,6 +4,8 @@ import { Card, Drawer, Skeleton } from "antd"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||
import { IodRegistryEntry } from "@/types/iod.ts"
|
||||
|
||||
// import { Drawer } from './Drawer.tsx'
|
||||
|
||||
const defaultData: IodRegistryEntry[] = [
|
||||
{
|
||||
name: "2019-2024年黄海清浅海域中河湖代数生物物种数据集",
|
||||
@@ -66,7 +68,8 @@ const ShowCard: React.FC<ShowCardProps> = ({
|
||||
</Card>
|
||||
)
|
||||
export const PlaygroundData = () => {
|
||||
const { messages, iodLoading, currentMessageId, iodSearch } = useMessageOption()
|
||||
const { messages, iodLoading, currentMessageId, iodSearch } =
|
||||
useMessageOption()
|
||||
|
||||
const data = useMemo<IodRegistryEntry[]>(() => {
|
||||
// 确保loading状态时数据大于3
|
||||
@@ -147,7 +150,12 @@ export const PlaygroundData = () => {
|
||||
width="33.33%">
|
||||
<div className="grid grid-cols-1 gap-3 overflow-y-auto">
|
||||
{data.map((item, index) => (
|
||||
<ShowCard key={item.doId} loading={iodLoading} record={item} truncate={false} />
|
||||
<ShowCard
|
||||
key={item.doId}
|
||||
loading={iodLoading}
|
||||
record={item}
|
||||
truncate={false}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Drawer>
|
||||
|
||||
91
src/components/Common/Playground/Drawer.tsx
Normal file
91
src/components/Common/Playground/Drawer.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
// Drawer.tsx
|
||||
import React, { useEffect } from "react"
|
||||
import styled from "styled-components"
|
||||
import { shadow } from "pdfjs-dist"
|
||||
|
||||
interface DrawerProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
children: React.ReactNode
|
||||
width?: string | number
|
||||
overlay?: boolean
|
||||
keydown?: boolean
|
||||
shadow?: boolean
|
||||
}
|
||||
|
||||
const DrawerOverlay = styled.div<{ isOpen: boolean }>`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
opacity: ${({ isOpen }) => (isOpen ? 1 : 0)};
|
||||
visibility: ${({ isOpen }) => (isOpen ? "visible" : "hidden")};
|
||||
transition:
|
||||
opacity 0.3s ease,
|
||||
visibility 0.3s ease;
|
||||
z-index: 1000;
|
||||
`
|
||||
|
||||
const DrawerContainer = styled.div<{
|
||||
isOpen: boolean
|
||||
width: string | number
|
||||
shadow: boolean
|
||||
}>`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: ${({ width }) => (typeof width === "number" ? `${width}px` : width)};
|
||||
background: #ffffff;
|
||||
box-shadow: ${shadow ? "-2px 0 8px rgba(0, 0, 0, 0.15)" : ""};
|
||||
transform: translateX(${({ isOpen }) => (isOpen ? "0" : "100%")});
|
||||
transition: transform 0.3s ease;
|
||||
z-index: 9999;
|
||||
overflow-y: auto;
|
||||
`
|
||||
|
||||
export const Drawer: React.FC<DrawerProps> = ({
|
||||
open,
|
||||
onClose,
|
||||
children,
|
||||
overlay = true,
|
||||
keydown = true,
|
||||
shadow = true,
|
||||
width = "300px"
|
||||
}) => {
|
||||
// 处理 Escape 键关闭抽屉
|
||||
useEffect(() => {
|
||||
const handleEscape = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape" && open) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
if (keydown) {
|
||||
document.addEventListener("keydown", handleEscape)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (keydown) {
|
||||
document.removeEventListener("keydown", handleEscape)
|
||||
}
|
||||
}
|
||||
}, [open, onClose, keydown])
|
||||
|
||||
// 处理点击遮罩层关闭抽屉
|
||||
const handleOverlayClick = (e: React.MouseEvent) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{overlay && <DrawerOverlay isOpen={open} onClick={handleOverlayClick} />}
|
||||
<DrawerContainer isOpen={open} width={width}>
|
||||
{children}
|
||||
</DrawerContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -22,6 +22,8 @@ import { PlusOutlined, RightOutlined } from "@ant-design/icons"
|
||||
import { qaPrompt } from "@/libs/playground.tsx"
|
||||
import { ProviderIcons } from "@/components/Common/ProviderIcon.tsx"
|
||||
import { fetchChatModels } from "@/services/ollama.ts"
|
||||
import logo from '@/assets/logo.png'
|
||||
|
||||
|
||||
const ModelIcon = () => {
|
||||
return (
|
||||
@@ -115,6 +117,7 @@ export const PlaygroundHistory = () => {
|
||||
{/*Header*/}
|
||||
<div className="flex flex-col overflow-y-hidden h-full">
|
||||
<div className="flex items-center justify-between transition-all duration-300 ease-in-out w-[250px]">
|
||||
<img src={logo} alt="logo" className="w-8" />
|
||||
<h2 className="text-xl font-bold text-zinc-700 dark:text-zinc-300 mr-3">
|
||||
<span className="text-[#d30100]">数联网</span>科创智能体
|
||||
</h2>
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import React, { useMemo } from "react"
|
||||
import { Card } from "antd"
|
||||
|
||||
// 使用 CSS-in-JS 方式
|
||||
import { Avatar, Card } from "antd"
|
||||
import { AnimatePresence, motion } from "framer-motion" // 使用 CSS-in-JS 方式
|
||||
import styled, { keyframes } from "styled-components"
|
||||
import CountUp from "react-countup"
|
||||
import { TalentPoolIcon } from "@/components/Icons/TalentPool .tsx"
|
||||
import { ResearchPaperIcon } from "@/components/Icons/ResearchPaper.tsx"
|
||||
import { DataProjectIcon } from "@/components/Icons/DataProject.tsx"
|
||||
import { DatasetIcon } from "@/components/Icons/Dataset.tsx"
|
||||
import { TechCompanyIcon } from "@/components/Icons/TechCompany.tsx"
|
||||
import { ResearchInstitutesIcon } from "@/components/Icons/ResearchInstitutes.tsx"
|
||||
import { NSDCIcon } from "@/components/Icons/NSDC.tsx"
|
||||
|
||||
const rotate = keyframes`
|
||||
0% {
|
||||
@@ -25,6 +32,7 @@ const breathe = keyframes`
|
||||
}
|
||||
`
|
||||
|
||||
// 花瓣
|
||||
const CircleElement = styled.div<{ delay: number; playing: boolean }>`
|
||||
position: absolute;
|
||||
width: 300px;
|
||||
@@ -39,15 +47,29 @@ const CircleElement = styled.div<{ delay: number; playing: boolean }>`
|
||||
${breathe} 2s infinite alternate;
|
||||
animation-delay: ${(props) => props.delay}s;
|
||||
animation-play-state: ${(props) => (props.playing ? "running" : "paused")};
|
||||
animation-duration: 3s; /* 添加动画总持续时间 */
|
||||
animation-fill-mode: forwards; /* 保持动画结束时的状态 */
|
||||
`
|
||||
|
||||
const SuccessIcon = () => {
|
||||
const FrostedGlassCard = styled(Card)`
|
||||
background: rgba(255, 255, 255, 0.25) !important;
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
`
|
||||
|
||||
const SuccessIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="w-full h-full text-green-500">
|
||||
className="text-green-500"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M9 12L11 14L15 10M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z"
|
||||
stroke="currentColor"
|
||||
@@ -57,9 +79,12 @@ const SuccessIcon = () => {
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const LoadingIcon = () => {
|
||||
const LoadingIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon animate-spin"
|
||||
@@ -67,16 +92,15 @@ const LoadingIcon = () => {
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="29588"
|
||||
width="18"
|
||||
height="18">
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M483.712 888.064a52.437333 52.437333 0 1 1 52.48 52.352 52.394667 52.394667 0 0 1-52.48-52.352z m-235.434667-53.76a65.578667 65.578667 0 1 1 46.421334 19.242667 65.962667 65.962667 0 0 1-46.378667-19.242667z m499.584-16.597333a41.984 41.984 0 0 1 59.264-59.434667 42.282667 42.282667 0 0 1 0 59.434667 41.941333 41.941333 0 0 1-59.264 0zM112.853333 546.602667a81.92 81.92 0 1 1 81.92 81.92 81.834667 81.834667 0 0 1-81.92-81.877334z m731.008 0a33.536 33.536 0 1 1 33.493334 33.578666 33.578667 33.578667 0 0 1-33.450667-33.536zM222.208 377.6a102.4 102.4 0 1 1 72.533333 29.866667 102.869333 102.869333 0 0 1-72.533333-29.824z m536.32-53.504a26.666667 26.666667 0 1 1 18.816 7.936 26.368 26.368 0 0 1-18.773333-7.893333zM414.378667 205.184a121.642667 121.642667 0 1 1 121.813333 121.6A121.728 121.728 0 0 1 414.378667 205.226667z"
|
||||
p-id="29589"
|
||||
fill="#4284f6"></path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
})
|
||||
const SearchIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
@@ -95,11 +119,110 @@ const SearchIcon = () => {
|
||||
)
|
||||
}
|
||||
|
||||
// 自定义统计卡片组件
|
||||
const StatCard: React.FC<{
|
||||
number: number
|
||||
unit?: string
|
||||
label: string
|
||||
decimals?: number
|
||||
icon: React.ReactNode
|
||||
}> = ({ number, unit, label, decimals, icon }) => {
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col items-center justify-center p-3 rounded-xl shadow-sm bg-[rgba(240,245,255,0.3)] backdrop-blur-sm border border-[rgba(200,220,255,0.25)]
|
||||
">
|
||||
<Avatar size={40} className="!bg-[#3581e3b3]" icon={icon} />
|
||||
|
||||
<div className="text-lg font-bold text-[#f00000]">
|
||||
<CountUp
|
||||
end={number}
|
||||
duration={2.5}
|
||||
separator=","
|
||||
decimals={decimals}
|
||||
/>
|
||||
{unit}
|
||||
</div>
|
||||
<div className="text-sm text-[#3581e3] mt-1 flex items-center gap-2">
|
||||
{" "}
|
||||
{label}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 主组件
|
||||
export const StatisticGrid: React.FC = () => {
|
||||
return (
|
||||
<div className="p-6 min-h-screen">
|
||||
{/* 第一行:3 个卡片 */}
|
||||
<div className="grid grid-cols-3 gap-6 mb-6">
|
||||
<StatCard
|
||||
icon={<NSDCIcon className="w-6 h-6 text-white" color="#3581e3" />}
|
||||
number={11}
|
||||
unit="家"
|
||||
label="国家科学数据中心"
|
||||
/>
|
||||
<StatCard
|
||||
icon={
|
||||
<ResearchInstitutesIcon
|
||||
className="w-6 h-6 text-white"
|
||||
color="#3581e3"
|
||||
/>
|
||||
}
|
||||
number={763}
|
||||
unit="家"
|
||||
label="高等院校和科研机构"
|
||||
/>
|
||||
<StatCard
|
||||
icon={
|
||||
<TechCompanyIcon className="w-6 h-6 text-white" color="#3581e3" />
|
||||
}
|
||||
number={2.1}
|
||||
decimals={1}
|
||||
unit="万"
|
||||
label="科技型企业"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 第二行:4 个卡片 */}
|
||||
<div className="grid grid-cols-4 gap-6">
|
||||
<StatCard
|
||||
icon={<DatasetIcon className="w-6 h-6 text-white" color="#3581e3" />}
|
||||
number={537163}
|
||||
label="数据集"
|
||||
/>
|
||||
<StatCard
|
||||
icon={
|
||||
<DataProjectIcon className="w-6 h-6 text-white" color="#3581e3" />
|
||||
}
|
||||
number={183729}
|
||||
label="数据项目"
|
||||
/>
|
||||
<StatCard
|
||||
icon={
|
||||
<ResearchPaperIcon className="w-6 h-6 text-white" color="#3581e3" />
|
||||
}
|
||||
number={1380026}
|
||||
label="科研论文"
|
||||
/>
|
||||
<StatCard
|
||||
icon={
|
||||
<TalentPoolIcon className="w-6 h-6 text-white" color="#3581e3" />
|
||||
}
|
||||
number={2}
|
||||
unit="万"
|
||||
label="科创人才"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const PlaygroundIodRelevant: React.FC = () => {
|
||||
const { messages, iodLoading, currentMessageId, iodSearch } =
|
||||
useMessageOption()
|
||||
|
||||
const showDescription = useMemo(() => {
|
||||
const showSearchData = useMemo(() => {
|
||||
return iodSearch && messages.length > 0 && !iodLoading
|
||||
}, [iodSearch, messages, iodLoading])
|
||||
|
||||
@@ -111,17 +234,34 @@ export const PlaygroundIodRelevant: React.FC = () => {
|
||||
{
|
||||
title: (
|
||||
<p className="font-extrabold">
|
||||
已连接<span className="text-[#f00000]"> 11家 </span>
|
||||
国家科学数据中心的
|
||||
<span className="text-[#f00000]"> 500000+个 </span>科学数据集中
|
||||
已在
|
||||
<span className="text-[#f00000]">
|
||||
<CountUp end={29} duration={2.5} separator="," />个
|
||||
</span>
|
||||
国家和省部级科学数据中心、
|
||||
<span className="text-[#f00000]">
|
||||
{" "}
|
||||
<CountUp end={55} duration={2.5} separator="," />所
|
||||
</span>
|
||||
高等院校的
|
||||
<span className="text-[#f00000]">
|
||||
<CountUp decimals={1} end={53.7} duration={2.5} separator="," />
|
||||
万个
|
||||
</span>
|
||||
科学数据集中进行搜索
|
||||
</p>
|
||||
),
|
||||
description: showDescription ? (
|
||||
description: showSearchData ? (
|
||||
<p>
|
||||
已发现
|
||||
<span className="text-green-700">
|
||||
{" "}
|
||||
{currentMessage?.iodSources.data.total}个{" "}
|
||||
<CountUp
|
||||
end={currentMessage?.iodSources.data.total ?? 0}
|
||||
duration={2.5}
|
||||
separator=","
|
||||
/>
|
||||
个{" "}
|
||||
</span>
|
||||
数据集
|
||||
</p>
|
||||
@@ -132,18 +272,32 @@ export const PlaygroundIodRelevant: React.FC = () => {
|
||||
{
|
||||
title: (
|
||||
<p className="font-extrabold">
|
||||
已连接<span className="text-[#f00000]"> 1000000+篇 </span>论文和
|
||||
<span className="text-[#f00000]"> 50000+个 </span>科创场景
|
||||
已在
|
||||
<span className="text-[#f00000]">
|
||||
<CountUp end={138} duration={2.5} separator="," />
|
||||
万篇
|
||||
</span>
|
||||
学术论文、
|
||||
<span className="text-[#f00000]">
|
||||
<CountUp end={18.3} decimals={1} duration={2.5} separator="," />
|
||||
万个
|
||||
</span>
|
||||
数据项目中进行搜索
|
||||
</p>
|
||||
),
|
||||
description: showDescription ? (
|
||||
description: showSearchData ? (
|
||||
<p>
|
||||
已发现
|
||||
<span className="text-green-700">
|
||||
{" "}
|
||||
{currentMessage?.iodSources.scenario.total}个{" "}
|
||||
<CountUp
|
||||
end={currentMessage?.iodSources.scenario.total ?? 0}
|
||||
duration={2.5}
|
||||
separator=","
|
||||
/>
|
||||
个{" "}
|
||||
</span>
|
||||
科创场景
|
||||
场景
|
||||
</p>
|
||||
) : (
|
||||
""
|
||||
@@ -152,19 +306,36 @@ export const PlaygroundIodRelevant: React.FC = () => {
|
||||
{
|
||||
title: (
|
||||
<p className="font-extrabold">
|
||||
已连接<span className="text-[#f00000]"> 1000+位 </span>智库专家
|
||||
<span className="text-[#f00000]"> 763家 </span>高校和科研机构和
|
||||
<span className="text-[#f00000]"> 21000+家 </span>科技企业
|
||||
正在
|
||||
<span className="text-[#f00000]">
|
||||
<CountUp end={763} duration={2.5} separator="," />家
|
||||
</span>
|
||||
高等院校和科研机构、
|
||||
<span className="text-[#f00000]">
|
||||
{" "}
|
||||
<CountUp end={2.1} decimals={1} duration={2.5} separator="," />万
|
||||
</span>
|
||||
家科技型企业、
|
||||
<span className="text-[#f00000]">
|
||||
{" "}
|
||||
<CountUp end={2} duration={2.5} separator="," />万
|
||||
</span>
|
||||
科技人才中进行搜索
|
||||
</p>
|
||||
),
|
||||
description: showDescription ? (
|
||||
description: showSearchData ? (
|
||||
<p>
|
||||
已发现
|
||||
<span className="text-green-700">
|
||||
{" "}
|
||||
{currentMessage?.iodSources.organization.total}个{" "}
|
||||
<CountUp
|
||||
end={currentMessage?.iodSources.organization.total ?? 0}
|
||||
duration={2.5}
|
||||
separator=","
|
||||
/>
|
||||
个{" "}
|
||||
</span>
|
||||
科技企业
|
||||
组织
|
||||
</p>
|
||||
) : (
|
||||
""
|
||||
@@ -180,7 +351,7 @@ export const PlaygroundIodRelevant: React.FC = () => {
|
||||
className="flex flex-col h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] translate-y-[-2px] !bg-[#f0f9ff]">
|
||||
<div className="h-full flex flex-col relative">
|
||||
{/* 花瓣效果 */}
|
||||
<div className="absolute inset-0 pointer-events-none z-0 overflow-hidden">
|
||||
<div className={`absolute inset-0 pointer-events-none z-0 overflow-hidden ${showSearchData ? '' : ''}`}>
|
||||
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-64 h-64">
|
||||
<CircleElement delay={0} playing={true} />
|
||||
<CircleElement delay={1} playing={true} />
|
||||
@@ -200,41 +371,71 @@ export const PlaygroundIodRelevant: React.FC = () => {
|
||||
{/*</button>*/}
|
||||
</h2>
|
||||
<p className="text-sm text-[#1a3c87] mt-1 text-center">
|
||||
下面是在数联网上进行深度搜索的相关内容
|
||||
下面是在数联网上进行深度搜索得到的科创相关数据、场景和团队
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="space-y-2 flex-1 overflow-y-auto">
|
||||
{data.map((item, index) => (
|
||||
<Card
|
||||
className="[&_.ant-card-body]:!p-3 [&_.ant-card-body]:h-full shadow-md h-[88px]"
|
||||
key={index}>
|
||||
<div
|
||||
className={`flex flex-col gap-2 h-full items-start ${showDescription ? "justify-start" : "justify-center"}`}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={`${showDescription ? "w-5 h-5" : "w-6 h-6"}`}>
|
||||
{iodSearch && iodLoading ? (
|
||||
<LoadingIcon />
|
||||
) : (
|
||||
<SuccessIcon />
|
||||
)}
|
||||
</div>
|
||||
<p
|
||||
className={`text-gray-700 ${showDescription ? "text-sm" : "text-lg"}`}>
|
||||
{item.title}
|
||||
</p>
|
||||
</div>
|
||||
{item.description && (
|
||||
<div className="flex-1">
|
||||
<p className="text-xs text-gray-500 mt-1 pl-7">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
{showSearchData ? (
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key="search-results"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="space-y-2 flex-1 overflow-y-auto">
|
||||
{data.map((item, index) => (
|
||||
<FrostedGlassCard
|
||||
className="[&_.ant-card-body]:!p-3 [&_.ant-card-body]:h-full shadow-md min-h-[88px]"
|
||||
key={index}>
|
||||
<div
|
||||
className={`flex flex-col gap-2 h-full items-start ${showSearchData ? "justify-start" : "justify-center"}`}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div>
|
||||
{iodSearch && iodLoading ? (
|
||||
<LoadingIcon
|
||||
width={showSearchData ? 16 : 22}
|
||||
height={showSearchData ? 16 : 22}
|
||||
/>
|
||||
) : (
|
||||
<SuccessIcon
|
||||
width={showSearchData ? 16 : 22}
|
||||
height={showSearchData ? 16 : 22}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<p
|
||||
className={`text-gray-700 ${showSearchData ? "text-sm" : "text-lg"}`}>
|
||||
{item.title}
|
||||
</p>
|
||||
</div>
|
||||
{item.description && (
|
||||
<div className="flex-1">
|
||||
<p className="text-xs text-gray-500 mt-1 pl-7">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</FrostedGlassCard>
|
||||
))}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
) : (
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key="statistic-grid"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="flex-1 overflow-y-auto">
|
||||
<StatisticGrid />
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user