feat: 添加 CloudWord 组件
This commit is contained in:
@@ -0,0 +1,112 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
interface VisualConfig {
|
||||||
|
color: 'primary' | 'secondary' | 'accent' | 'gray';
|
||||||
|
size: string;
|
||||||
|
border: string;
|
||||||
|
rotation?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KeywordData {
|
||||||
|
id: number;
|
||||||
|
word: string;
|
||||||
|
trendScore: number;
|
||||||
|
description: string;
|
||||||
|
detailPoints: string[];
|
||||||
|
visualConfig: VisualConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CloudWordProps {
|
||||||
|
data: KeywordData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 颜色映射
|
||||||
|
const colorMap: Record<string, string> = {
|
||||||
|
primary: 'bg-primary',
|
||||||
|
secondary: 'bg-secondary',
|
||||||
|
accent: 'bg-accent',
|
||||||
|
gray: 'bg-gray-100 dark:bg-gray-700',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function CloudWord({ data }: CloudWordProps) {
|
||||||
|
const { word, visualConfig, description, detailPoints } = data;
|
||||||
|
const { color, size, border, rotation } = visualConfig;
|
||||||
|
const colorClass = colorMap[color];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
'cloud-word',
|
||||||
|
'inline-block',
|
||||||
|
'whitespace-nowrap',
|
||||||
|
'relative',
|
||||||
|
'transition-all',
|
||||||
|
'duration-200',
|
||||||
|
'cursor-pointer',
|
||||||
|
border,
|
||||||
|
'border-black',
|
||||||
|
colorClass,
|
||||||
|
'px-6',
|
||||||
|
'py-3',
|
||||||
|
'rounded-full',
|
||||||
|
size,
|
||||||
|
'font-black',
|
||||||
|
'shadow-hard',
|
||||||
|
rotation,
|
||||||
|
'hover:scale-105',
|
||||||
|
'hover:z-10'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{word}
|
||||||
|
<WordPopover
|
||||||
|
title={word}
|
||||||
|
description={description}
|
||||||
|
points={detailPoints}
|
||||||
|
titleColor={color}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WordPopoverProps {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
points: string[];
|
||||||
|
titleColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function WordPopover({ title, description, points, titleColor }: WordPopoverProps) {
|
||||||
|
const titleColorClass = colorMap[titleColor] || 'bg-gray-100';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="absolute bottom-[calc(100%+12px)] left-1/2 -translate-x-1/2 w-48 bg-white dark:bg-gray-800 border-2 border-black shadow-hard-sm opacity-0 pointer-events-none transition-all duration-300 translate-y-2 z-50 text-left group-hover:opacity-100 group-hover:translate-y-0 group-hover:pointer-events-auto">
|
||||||
|
{/* 标题栏 */}
|
||||||
|
<div className={cn(
|
||||||
|
'text-black dark:text-white font-display font-bold p-2 border-b-2 border-black text-sm uppercase',
|
||||||
|
titleColorClass
|
||||||
|
)}>
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 内容区域 */}
|
||||||
|
<div className="p-3 space-y-2 text-xs font-medium dark:text-gray-100">
|
||||||
|
<div className="flex gap-2 items-start">
|
||||||
|
<span>•</span>
|
||||||
|
<span>{description}</span>
|
||||||
|
</div>
|
||||||
|
{points.map((point, i) => (
|
||||||
|
<div key={i} className="flex gap-2 items-start">
|
||||||
|
<span>•</span>
|
||||||
|
<span>{point}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 箭头 */}
|
||||||
|
<div className="absolute -bottom-2 left-1/2 -translate-x-1/2 border-l-[8px] border-l-transparent border-r-[8px] border-r-transparent border-t-[8px] border-t-black" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user