flnxi-Wu/lib/ui-styles.ts
WuYongJie 8705b9846a
Some checks failed
CI Check / check (push) Has been cancelled
feat: 统一弹窗样式系统与分类管理优化
- 创建统一的Modal组件,采用Apple风格设计
- 实现毛玻璃效果、圆角、动画和细边框描边
- 修复弹窗打开时背景滚动问题
- 修复分类管理中重复卡片和数组字符串显示问题
- 优化选项解析逻辑,添加去重和过滤机制
2025-11-25 09:37:00 +08:00

212 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 统一的 UI 样式工具类
* 苹果风格 + 炫彩元素
*
* ⚠️ 注意:此文件已重构为支持两套独立配置(浅色/深色)
*
* 推荐使用方式:
* 1. 在新组件中使用 `useThemeStyles()` Hook(推荐)
* 2. 在服务端组件或需要向后兼容的地方,继续使用此文件的导出
*
* 迁移步骤:
* - 将组件改为客户端组件('use client')
* - 使用 `const styles = useThemeStyles()` 替代直接导入
* - 使用 `styles.cardStyles.base` 替代 `cardStyles.base`
*/
import { gradientBackgrounds, glowEffects, cardAnimations } from './ui-animations'
// 导出主题 Hook(推荐使用)
export { useThemeStyles, useTheme } from './hooks/useThemeStyles'
// 导出两套独立的样式配置
export { lightStyles, darkStyles } from './ui-styles-theme-exports'
// 为了向后兼容,保留原有导出(包含 dark: 前缀)
// ⚠️ 这些样式会在深色模式下正确工作,但代码较长
// 建议逐步迁移到 useThemeStyles Hook
export const cardStyles = {
// 基础卡片 - 确保深色模式完全适配(兼容模式)
base: 'rounded-2xl border border-slate-200/70 dark:border-slate-700/70 bg-white dark:bg-slate-800 backdrop-blur-sm shadow-lg shadow-slate-200/50 dark:shadow-black/50 p-6 transition-all duration-300',
// 悬停卡片(带提升效果)- 确保深色模式完全适配
hover: 'rounded-2xl border border-slate-200/70 dark:border-slate-700/70 bg-white dark:bg-slate-800 backdrop-blur-sm shadow-lg shadow-slate-200/50 dark:shadow-black/50 hover:shadow-xl hover:shadow-indigo-500/20 dark:hover:shadow-indigo-500/30 hover:-translate-y-1 transition-all duration-300 p-6',
// 大卡片(带炫彩光晕)- 确保深色模式完全适配
large: 'rounded-3xl border border-slate-200/70 dark:border-slate-700/70 bg-white dark:bg-slate-800 backdrop-blur-md shadow-xl shadow-slate-200/60 dark:shadow-black/60 hover:shadow-2xl hover:shadow-indigo-500/25 dark:hover:shadow-indigo-500/35 transition-all duration-500 p-8',
// 扁平卡片 - 确保深色模式完全适配
flat: 'rounded-2xl border border-slate-200/70 dark:border-slate-700/70 bg-white dark:bg-slate-800 backdrop-blur-sm p-6 transition-all duration-300',
// 炫彩卡片(带渐变背景)- 确保深色模式完全适配
rainbow: `rounded-2xl border border-slate-200/70 dark:border-slate-700/70 ${gradientBackgrounds.rainbowSubtle} backdrop-blur-sm shadow-lg shadow-purple-500/20 dark:shadow-purple-500/30 hover:shadow-xl hover:shadow-purple-500/30 dark:hover:shadow-purple-500/40 hover:-translate-y-1 transition-all duration-300 p-6`,
// 玻璃态卡片 - 确保深色模式完全适配
glass: `rounded-2xl ${gradientBackgrounds.glass} shadow-lg shadow-slate-200/50 dark:shadow-black/50 hover:shadow-xl hover:shadow-indigo-500/20 dark:hover:shadow-indigo-500/30 transition-all duration-300 p-6`,
// 霓虹卡片 - 确保深色模式完全适配
neon: 'rounded-2xl border-2 border-cyan-400/50 dark:border-cyan-400/70 bg-white dark:bg-slate-800 backdrop-blur-sm shadow-lg shadow-cyan-500/30 dark:shadow-cyan-500/40 hover:shadow-xl hover:shadow-cyan-500/50 dark:hover:shadow-cyan-500/60 transition-all duration-300 p-6',
} as const
export const buttonStyles = {
// 主要按钮(带炫彩光晕)- 确保深色模式完全适配
primary: 'px-6 py-3 rounded-xl bg-indigo-600 dark:bg-indigo-500 text-white font-medium shadow-lg shadow-indigo-500/40 dark:shadow-indigo-500/50 hover:bg-indigo-700 dark:hover:bg-indigo-600 hover:shadow-xl hover:shadow-indigo-500/50 dark:hover:shadow-indigo-500/60 active:scale-95 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed',
// 次要按钮 - 确保深色模式完全适配
secondary: 'px-6 py-3 rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 backdrop-blur-sm text-slate-700 dark:text-slate-200 font-medium hover:bg-slate-50 dark:hover:bg-slate-700 hover:border-indigo-400 dark:hover:border-indigo-400 active:scale-95 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed',
// 幽灵按钮 - 确保深色模式完全适配
ghost: 'px-6 py-3 rounded-xl text-slate-700 dark:text-slate-200 font-medium hover:bg-slate-100 dark:hover:bg-slate-800 active:scale-95 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed',
// 危险按钮 - 确保深色模式完全适配(浅色模式使用更深的红色以增强对比度)
danger: 'px-6 py-3 rounded-xl bg-red-700 dark:bg-red-500 text-white font-medium shadow-lg shadow-red-600/70 dark:shadow-red-500/50 hover:bg-red-800 dark:hover:bg-red-600 hover:shadow-xl hover:shadow-red-600/80 dark:hover:shadow-red-500/60 active:scale-95 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed',
// 成功按钮 - 确保深色模式完全适配
success: 'px-6 py-3 rounded-xl bg-emerald-600 dark:bg-emerald-500 text-white font-medium shadow-lg shadow-emerald-500/40 dark:shadow-emerald-500/50 hover:bg-emerald-700 dark:hover:bg-emerald-600 hover:shadow-xl hover:shadow-emerald-500/50 dark:hover:shadow-emerald-500/60 active:scale-95 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed',
// 炫彩渐变按钮
rainbow: 'px-6 py-3 rounded-xl bg-gradient-to-r from-pink-500 via-purple-500 to-indigo-500 text-white font-medium shadow-lg shadow-purple-500/40 hover:from-pink-600 hover:via-purple-600 hover:to-indigo-600 hover:shadow-xl hover:shadow-purple-500/50 active:scale-95 transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed',
// 霓虹按钮
neon: 'px-6 py-3 rounded-xl bg-cyan-500 text-white font-medium shadow-lg shadow-cyan-500/50 hover:bg-cyan-400 hover:shadow-xl hover:shadow-cyan-500/70 active:scale-95 transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed',
// 玻璃态按钮
glass: 'px-6 py-3 rounded-xl bg-white/10 dark:bg-white/5 backdrop-blur-md border border-white/20 text-white font-medium hover:bg-white/20 dark:hover:bg-white/10 hover:border-white/30 active:scale-95 transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed',
} as const
export const buttonModifiers = {
compact: 'px-3 py-1.5 text-sm',
compactXs: 'px-2 py-1 text-xs',
icon: 'p-2 rounded-xl',
block: 'w-full justify-center',
pill: 'rounded-full',
} as const
export const inputStyles = {
// 基础输入框 - 确保深色模式完全适配
base: 'w-full px-4 py-3 rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-white placeholder:text-slate-400 dark:placeholder:text-slate-400 focus:border-indigo-500 dark:focus:border-indigo-400 focus:ring-2 focus:ring-indigo-200 dark:focus:ring-indigo-800 outline-none transition-all',
// 错误输入框 - 确保深色模式完全适配
error: 'w-full px-4 py-3 rounded-xl border-2 border-red-500 dark:border-red-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-white placeholder:text-slate-400 dark:placeholder:text-slate-400 focus:ring-2 focus:ring-red-200 dark:focus:ring-red-800 outline-none transition-all',
} as const
export const inputModifiers = {
compact: 'px-3 py-2 text-sm rounded-lg',
compactXs: 'px-2.5 py-1.5 text-xs rounded-lg',
} as const
export const navItemStyles = {
// 基础导航项 - 确保深色模式完全适配
base: 'px-4 py-3 rounded-xl border border-slate-200/70 dark:border-slate-700/70 bg-white dark:bg-slate-800 backdrop-blur-sm text-slate-700 dark:text-slate-200 transition-all duration-300',
// 悬停导航项(带光晕效果)- 确保深色模式完全适配
hover: 'px-4 py-3 rounded-xl border border-slate-200/70 dark:border-slate-700/70 bg-white dark:bg-slate-800 backdrop-blur-sm hover:border-indigo-400 dark:hover:border-indigo-400 hover:bg-indigo-50 dark:hover:bg-indigo-900/30 hover:shadow-md hover:shadow-indigo-500/20 dark:hover:shadow-indigo-500/30 text-slate-700 dark:text-slate-200 transition-all duration-300',
// 激活导航项(带炫彩光晕)- 确保深色模式完全适配
active: 'px-4 py-3 rounded-xl border-2 border-indigo-500 dark:border-indigo-400 bg-indigo-50 dark:bg-indigo-900/40 backdrop-blur-sm text-indigo-700 dark:text-indigo-200 shadow-lg shadow-indigo-500/30 dark:shadow-indigo-500/40 transition-all duration-300',
// 炫彩导航项 - 确保深色模式完全适配
rainbow: 'px-4 py-3 rounded-xl border border-slate-200/70 dark:border-slate-700/70 bg-gradient-to-r from-pink-50/50 via-purple-50/50 to-indigo-50/50 dark:from-pink-900/20 dark:via-purple-900/20 dark:to-indigo-900/20 backdrop-blur-sm hover:shadow-md hover:shadow-purple-500/20 dark:hover:shadow-purple-500/30 text-slate-700 dark:text-slate-200 transition-all duration-300',
// 标签页导航项 - 确保深色模式完全适配
tabBase: 'relative pb-4 px-5 font-medium transition-all duration-300 ease-out text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white',
tabActive: 'relative pb-4 px-5 font-medium transition-all duration-300 ease-out text-slate-900 dark:text-white',
} as const
export const badgeStyles = {
base: 'px-3 py-1 rounded-lg text-xs font-medium',
primary: 'px-3 py-1 rounded-lg bg-indigo-100 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300 text-xs font-medium',
success: 'px-3 py-1 rounded-lg bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300 text-xs font-medium',
warning: 'px-3 py-1 rounded-lg bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-300 text-xs font-medium',
danger: 'px-3 py-1 rounded-lg bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 text-xs font-medium',
} as const
export const spacingStyles = {
section: 'space-y-8', // 区块间距
card: 'space-y-6', // 卡片内间距
form: 'space-y-4', // 表单元素间距
compact: 'space-y-2', // 紧密间距
} as const
/**
* 组合多个样式类
*/
export function cn(...classes: (string | undefined | false | null)[]): string {
return classes.filter(Boolean).join(' ')
}
/**
* 运行时根据主题获取样式配置
* ⚠️ 不推荐使用,建议在组件中使用 useThemeStyles Hook
*
* @param theme - 主题 'light' | 'dark'
* @returns 对应主题的样式配置
*/
export function getThemeStyles(theme: 'light' | 'dark') {
// 动态导入以避免类型冲突
if (theme === 'dark') {
return require('./ui-styles-dark')
}
return require('./ui-styles-light')
}
/**
* Section 显示动画类生成器(来自外观设置)
* @param isVisible - 是否可见
* @returns 组合后的样式类
*/
export function sectionRevealClass(isVisible: boolean): string {
return cn(
'transition-all duration-700 ease-out transform',
isVisible ? 'opacity-100 translate-y-0 scale-100' : 'opacity-0 translate-y-6 scale-95'
)
}
/**
* 浮动光球样式类生成器(来自外观设置)
* @param theme - 主题 'light' | 'dark'
* @param position - 位置 'topRight' | 'bottomLeft'
* @returns 样式类字符串
*/
export function floatingOrbClass(theme: 'light' | 'dark', position: 'topRight' | 'bottomLeft'): string {
const baseClasses = 'absolute rounded-full blur-3xl pointer-events-none'
const positionClasses = position === 'topRight'
? '-top-8 -right-8 h-24 w-24 animate-pulse-slow'
: '-bottom-8 -left-8 h-20 w-20 animate-[pulse_8s_ease-in-out_infinite]'
const colorClasses = position === 'topRight'
? (theme === 'dark' ? 'bg-indigo-500/15' : 'bg-indigo-400/20')
: (theme === 'dark' ? 'bg-purple-500/15' : 'bg-purple-400/20')
return cn(baseClasses, positionClasses, colorClasses)
}
/**
* Hero 动态背景样式生成器(来自外观设置)
* @param isMouseOver - 鼠标是否悬停
* @param glowPos - 光晕位置 { x: number, y: number }
* @param theme - 主题 'light' | 'dark'
* @param baseGradient - 基础渐变背景
* @returns 背景样式对象
*/
export function getHeroDynamicBackground(
isMouseOver: boolean,
glowPos: { x: number; y: number },
theme: 'light' | 'dark',
baseGradient: string
): { backgroundImage: string; transition: string } {
if (isMouseOver) {
return {
backgroundImage: `radial-gradient(circle 300px at ${glowPos.x}% ${glowPos.y}%, ${
theme === 'dark'
? 'rgba(255,255,255,0.2), rgba(255,255,255,0.1), rgba(255,255,255,0.05), transparent 75%'
: 'rgba(255,255,255,0.3), rgba(255,255,255,0.15), rgba(255,255,255,0.08), transparent 75%'
}), ${baseGradient}`,
transition: 'background-image 0.4s ease-out',
}
}
return {
backgroundImage: baseGradient,
transition: 'background-image 0.4s ease-out',
}
}