Files
RN_Template/RN_TEMPLATE/app/components/Icon.tsx

199 lines
4.0 KiB
TypeScript
Raw Normal View History

2026-02-05 13:16:05 +08:00
import { ComponentType } from "react"
import {
StyleProp,
TouchableOpacity,
TouchableOpacityProps,
View,
ViewProps,
ViewStyle,
} from "react-native"
import {
ArrowLeft,
BarChart2,
Bell,
Bug,
Camera,
Check,
ChevronLeft,
ChevronRight,
Copy,
CreditCard,
Eye,
EyeOff,
FileText,
Github,
Globe,
HandMetal,
Heart,
Home,
Info,
Layers,
Lock,
LogOut,
Mail,
MapPin,
Menu,
MessageSquare,
Mic,
Monitor,
Moon,
MoreHorizontal,
Pencil,
Settings,
Shield,
Smartphone,
Sun,
Tablet,
Tag,
User,
Users,
X,
LucideProps,
} from "lucide-react-native"
import { useAppTheme } from "@/theme/context"
import { s } from "@/utils/responsive"
// Map icon names to Lucide components
const iconRegistry: Record<string, ComponentType<LucideProps>> = {
back: ArrowLeft,
barChart: BarChart2,
bell: Bell,
camera: Camera,
caretLeft: ChevronLeft,
caretRight: ChevronRight,
check: Check,
clap: HandMetal,
community: Users,
components: Layers,
copy: Copy,
creditCard: CreditCard,
debug: Bug,
fileText: FileText,
github: Github,
globe: Globe,
heart: Heart,
hidden: EyeOff,
home: Home,
info: Info,
ladybug: Bug,
lock: Lock,
logout: LogOut,
mail: Mail,
menu: Menu,
monitor: Monitor,
moon: Moon,
more: MoreHorizontal,
pencil: Pencil,
pin: MapPin,
podcast: Mic,
settings: Settings,
shield: Shield,
slack: MessageSquare,
smartphone: Smartphone,
sun: Sun,
tablet: Tablet,
tag: Tag,
user: User,
view: Eye,
x: X,
}
export type IconTypes = keyof typeof iconRegistry
type BaseIconProps = {
/**
* The name of the icon
*/
icon: IconTypes
/**
* An optional tint color for the icon
*/
color?: string
/**
* An optional size for the icon. If not provided, defaults to 24.
*/
size?: number
/**
* Style overrides for the icon container
*/
containerStyle?: StyleProp<ViewStyle>
/**
* Stroke width for the icon (default: 2)
*/
strokeWidth?: number
}
type PressableIconProps = Omit<TouchableOpacityProps, "style"> & BaseIconProps
type IconProps = Omit<ViewProps, "style"> & BaseIconProps
/**
* A component to render a registered icon.
* It is wrapped in a <TouchableOpacity />
* @see [Documentation and Examples]{@link https://docs.infinite.red/ignite-cli/boilerplate/app/components/Icon/}
* @param {PressableIconProps} props - The props for the `PressableIcon` component.
* @returns {JSX.Element} The rendered `PressableIcon` component.
*/
export function PressableIcon(props: PressableIconProps) {
const {
icon,
color,
size = s(24),
containerStyle: $containerStyleOverride,
strokeWidth = 2,
...pressableProps
} = props
const { theme } = useAppTheme()
const IconComponent = iconRegistry[icon]
if (!IconComponent) {
if (__DEV__) console.warn(`Icon "${icon}" not found in registry`)
return null
}
return (
<TouchableOpacity {...pressableProps} style={$containerStyleOverride}>
<IconComponent color={color ?? theme.colors.text} size={size} strokeWidth={strokeWidth} />
</TouchableOpacity>
)
}
/**
* A component to render a registered icon.
* It is wrapped in a <View />, use `PressableIcon` if you want to react to input
* @see [Documentation and Examples]{@link https://docs.infinite.red/ignite-cli/boilerplate/app/components/Icon/}
* @param {IconProps} props - The props for the `Icon` component.
* @returns {JSX.Element} The rendered `Icon` component.
*/
export function Icon(props: IconProps) {
const {
icon,
color,
size = s(24),
containerStyle: $containerStyleOverride,
strokeWidth = 2,
...viewProps
} = props
const { theme } = useAppTheme()
const IconComponent = iconRegistry[icon]
if (!IconComponent) {
if (__DEV__) console.warn(`Icon "${icon}" not found in registry`)
return null
}
return (
<View {...viewProps} style={$containerStyleOverride}>
<IconComponent color={color ?? theme.colors.text} size={size} strokeWidth={strokeWidth} />
</View>
)
}
export { iconRegistry }