42d3fedbaa
* fix(ci): fix release workflow artifact upload issue - Split artifact upload into 3 separate steps (Chrome, Firefox, Source) - Update download steps to match new artifact names - Add if-no-files-found: error for better error handling - Fix Node.js 20 compatibility issue with upload-artifact@v4 glob pattern * docs: 更新 AGENTS.md,添加测试工具和翻译文件结构说明 * docs: 更新 AGENTS.md,完善 CI 步骤和存储键名格式说明 * fix: remove unnecessary animations from StorageCleaner page - Remove entrance animations (animate-in, fade-in, zoom-in) from all components - Remove scale effect (active:scale-[0.99]) from clean button - Remove transition effects (transition-all, transition-colors) from OptionItem, StorageOptionsGrid, AutoRefreshToggle - Remove bouncing animation from error icon - Remove pulsing animation from warning banner - Keep animate-spin on button loading spinner as functional indicator * fix: remove unnecessary animations from RightClickRestorer page * fix: remove all unnecessary animations from all pages - Remove dead code animations (animate-in, fade-in, slide-in, zoom-in, shake) from tailwindcss-animate plugin (not installed) - Remove decorative transition effects (transition-all, transition-colors) from all page components - Remove scale effects (active:scale-95) from buttons - Remove bounce animations (animate-bounce) from icons - Keep functional animate-spin on loading spinners as they provide essential loading feedback Affected pages: Dashboard, Timestamp, Jwt, JsonTools, Base64Converter, HtmlToMarkdown, MarkdownToHtml, QrCode, TextStatistics --------- Co-authored-by: Ubuntu <ubuntu@localhost.localdomain>
108 lines
3.9 KiB
TypeScript
108 lines
3.9 KiB
TypeScript
import type { ComponentType } from 'react';
|
|
import React from 'react';
|
|
import type { LucideProps } from 'lucide-react';
|
|
import { ChevronRight } from 'lucide-react';
|
|
import type { PaletteColorKey } from '@/config/features';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
const PALETTE_COLORS: Record<PaletteColorKey, string> = {
|
|
primary: '13, 148, 136', // teal
|
|
success: '22, 163, 74', // green
|
|
warning: '217, 119, 6', // amber (存储清理的橙色轴)
|
|
error: '220, 38, 38', // red
|
|
secondary: '147, 51, 2 purple',
|
|
info: '37, 99, 235', // blue
|
|
};
|
|
|
|
export interface ToolCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
title: string;
|
|
description?: string;
|
|
snapshot?: React.ReactNode;
|
|
colorKey: PaletteColorKey;
|
|
icon: ComponentType<LucideProps>;
|
|
onNavigate: () => void;
|
|
}
|
|
|
|
export default function ToolCard({
|
|
title,
|
|
description,
|
|
snapshot,
|
|
colorKey,
|
|
icon: IconComponent,
|
|
onNavigate,
|
|
className,
|
|
...props
|
|
}: ToolCardProps) {
|
|
const rgbValues = PALETTE_COLORS[colorKey];
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
['--tool-color' as string]: rgbValues,
|
|
}}
|
|
/* 💡 核心修复点:
|
|
- 坚决不用 h-full 或固定高度,锁死 h-auto(高度自适应流),配合 py-4 px-4 牢牢把内容包裹在卡片体内。
|
|
- 废除原先会乱飘的内联 style 属性擦写,全权放权给 Tailwind 的声明式 hover 变体。
|
|
*/
|
|
className={cn(
|
|
'group relative rounded-xl border border-border/70 bg-card text-card-foreground p-4 h-auto flex flex-col items-stretch justify-start gap-3 shadow-sm select-none box-border',
|
|
'hover:bg-muted/30',
|
|
'hover:border-[rgba(var(--tool-color),0.45)]',
|
|
'hover:shadow-[0_8px_24px_-8px_rgba(var(--tool-color),0.14)] dark:hover:shadow-[0_8px_30px_-10px_rgba(var(--tool-color),0.25)]',
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
{/* 上半部分:核心信息交互排版轴 */}
|
|
<div className="flex items-center justify-between w-full relative min-w-0 min-h-[44px]">
|
|
<div className="flex gap-3 items-center min-w-0 flex-1 pr-2">
|
|
{/* 左侧圆形图标容器 */}
|
|
<div
|
|
className={cn(
|
|
'flex items-center justify-center w-10 h-10 rounded-xl shrink-0',
|
|
'bg-[rgba(var(--tool-color),0.08)] dark:bg-[rgba(var(--tool-color),0.12)]',
|
|
'text-[rgb(var(--tool-color))]',
|
|
)}
|
|
>
|
|
<IconComponent className="h-5 w-5 shrink-0" />
|
|
</div>
|
|
|
|
{/* 中间文字描述区:利用 flex-1 min-w-0 防御文本过长发生恶性撑开 */}
|
|
<div className="flex-1 min-w-0 flex flex-col">
|
|
<h4 className="font-bold text-sm tracking-tight text-foreground leading-snug">
|
|
{title}
|
|
</h4>
|
|
{description && (
|
|
<p className="text-[11px] font-medium text-muted-foreground/90 mt-0.5 leading-normal w-full truncate">
|
|
{description}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 右侧指示小箭头 */}
|
|
<div className="text-muted-foreground/40 group-hover:text-[rgb(var(--tool-color))] p-1 shrink-0 group-hover:translate-x-0.5">
|
|
<ChevronRight className="h-4 w-4" />
|
|
</div>
|
|
|
|
{/* 覆盖整个上半部分的绝对定位隐形跳转层(A11y 无障碍标准合规) */}
|
|
<button
|
|
type="button"
|
|
onClick={onNavigate}
|
|
aria-label={`进入 ${title}`}
|
|
className="absolute inset-0 w-full h-full cursor-pointer bg-transparent border-none opacity-0 focus-visible:outline-none"
|
|
/>
|
|
</div>
|
|
|
|
{/* 下半部分:未来的动态预览沙箱独立承载区 */}
|
|
{snapshot != null && (
|
|
<div className="mt-1 pt-3 border-t border-dashed border-border/80 w-full relative z-10 select-text">
|
|
{snapshot}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
ToolCard.displayName = 'ToolCard';
|