Files
gridpilot.gg/apps/website/ui/Icon.tsx
2026-01-18 23:24:30 +01:00

77 lines
2.1 KiB
TypeScript

import { LucideIcon } from 'lucide-react';
import React from 'react';
import { Box, BoxProps } from './Box';
export interface IconProps extends Omit<BoxProps<'div'>, 'children'> {
icon: LucideIcon | React.ReactNode;
size?: 3 | 3.5 | 4 | 5 | 6 | 7 | 8 | 10 | 12 | 16 | 'full' | number;
intent?: 'primary' | 'telemetry' | 'warning' | 'success' | 'critical' | 'high' | 'med' | 'low';
animate?: 'spin' | 'pulse' | 'none';
}
export function Icon({
icon: IconProp,
size = 4,
intent,
animate = 'none',
...props
}: IconProps) {
const sizeMap: Record<string | number, string> = {
3: '0.75rem',
3.5: '0.875rem',
4: '1rem',
5: '1.25rem',
6: '1.5rem',
7: '1.75rem',
8: '2rem',
10: '2.5rem',
12: '3rem',
16: '4rem',
'full': '100%'
};
const dimension = typeof size === 'string' ? sizeMap[size] : (sizeMap[size] || `${size * 0.25}rem`);
const intentColorMap: Record<string, string> = {
primary: 'var(--ui-color-intent-primary)',
telemetry: 'var(--ui-color-intent-telemetry)',
warning: 'var(--ui-color-intent-warning)',
success: 'var(--ui-color-intent-success)',
critical: 'var(--ui-color-intent-critical)',
high: 'var(--ui-color-text-high)',
med: 'var(--ui-color-text-med)',
low: 'var(--ui-color-text-low)',
};
const style: React.CSSProperties = {
width: dimension,
height: dimension,
color: intent ? intentColorMap[intent] : undefined,
};
const renderIcon = () => {
if (!IconProp) return null;
if (typeof IconProp === 'function' || (typeof IconProp === 'object' && 'render' in IconProp)) {
const LucideIconComponent = IconProp as LucideIcon;
return <LucideIconComponent size="100%" />;
}
return IconProp;
};
const animationClass = animate === 'spin' ? 'animate-spin' : (animate === 'pulse' ? 'animate-pulse' : '');
return (
<Box
display="inline-flex"
alignItems="center"
justifyContent="center"
style={style}
{...props}
>
<div className={`${animationClass} w-full h-full flex items-center justify-center`}>
{renderIcon()}
</div>
</Box>
);
}