199 lines
4.0 KiB
TypeScript
199 lines
4.0 KiB
TypeScript
|
|
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 }
|