61 lines
1.7 KiB
TypeScript
61 lines
1.7 KiB
TypeScript
import React from 'react';
|
||
import { icons, LucideProps } from 'lucide-react';
|
||
import { FolderKanban } from 'lucide-react';
|
||
|
||
interface DynamicIconProps extends Omit<LucideProps, 'ref'> {
|
||
/** 图标名称(Lucide React 图标名)或 emoji */
|
||
name?: string;
|
||
/** 默认图标(当找不到指定图标时显示) */
|
||
fallback?: React.ComponentType<LucideProps>;
|
||
}
|
||
|
||
/**
|
||
* 动态图标组件
|
||
*
|
||
* 支持:
|
||
* 1. Lucide React 的所有图标(通过图标名称字符串)
|
||
* 2. Emoji 表情
|
||
*
|
||
* @example
|
||
* ```tsx
|
||
* // 使用 Lucide 图标名称
|
||
* <DynamicIcon name="Calendar" className="h-4 w-4" />
|
||
*
|
||
* // 使用 emoji
|
||
* <DynamicIcon name="📅" />
|
||
*
|
||
* // 自定义默认图标
|
||
* <DynamicIcon name="NonExistent" fallback={Star} />
|
||
* ```
|
||
*/
|
||
const DynamicIcon: React.FC<DynamicIconProps> = ({
|
||
name,
|
||
fallback: FallbackIcon = FolderKanban,
|
||
className = "h-4 w-4",
|
||
...props
|
||
}) => {
|
||
if (!name) {
|
||
return <FallbackIcon className={className} {...props} />;
|
||
}
|
||
|
||
// 检测是否为 emoji(包括各种 Unicode emoji 范围)
|
||
const isEmoji = name.length <= 4 && /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{1F1E0}-\u{1F1FF}]/u.test(name);
|
||
|
||
if (isEmoji) {
|
||
return <span className="inline-flex items-center justify-center text-lg">{name}</span>;
|
||
}
|
||
|
||
// 从 lucide-react 的 icons 对象中动态获取图标组件
|
||
const IconComponent = icons[name as keyof typeof icons];
|
||
|
||
if (IconComponent) {
|
||
return <IconComponent className={className} {...props} />;
|
||
}
|
||
|
||
// 如果找不到,显示默认图标
|
||
return <FallbackIcon className={className} {...props} />;
|
||
};
|
||
|
||
export default DynamicIcon;
|
||
|