104 lines
3.2 KiB
TypeScript
104 lines
3.2 KiB
TypeScript
import React from 'react';
|
|
import { Pressable, StyleProp, TextStyle, View, ViewStyle } from 'react-native';
|
|
import Entypo from '@react-native-vector-icons/entypo';
|
|
import FontAwesome from '@react-native-vector-icons/fontawesome';
|
|
import FontAwesome6 from '@react-native-vector-icons/fontawesome6';
|
|
import Ionicons from '@react-native-vector-icons/ionicons';
|
|
import MaterialDesignIcons from '@react-native-vector-icons/material-design-icons';
|
|
import MaterialIcons from '@react-native-vector-icons/material-icons';
|
|
|
|
export type FontAwesomeIconName = React.ComponentProps<typeof FontAwesome>['name'];
|
|
export type FontAwesome6IconName = React.ComponentProps<typeof FontAwesome6>['name'];
|
|
export type IonIconName = React.ComponentProps<typeof Ionicons>['name'];
|
|
export type MaterialIconName = React.ComponentProps<typeof MaterialIcons>['name'];
|
|
export type MaterialDesignIconName = React.ComponentProps<typeof MaterialDesignIcons>['name'];
|
|
export type EntypoIconName = React.ComponentProps<typeof Entypo>['name'];
|
|
|
|
type IconType = 'font-awesome' | 'font-awesome-6' | 'ionicons' | 'material' | 'material-community' | 'entypo';
|
|
type IconComponentType = React.ComponentType<any>;
|
|
|
|
type IconNameFor<T extends IconType> = T extends 'font-awesome'
|
|
? FontAwesomeIconName
|
|
: T extends 'font-awesome-6'
|
|
? FontAwesome6IconName
|
|
: T extends 'ionicons'
|
|
? IonIconName
|
|
: T extends 'material'
|
|
? MaterialIconName
|
|
: T extends 'material-community'
|
|
? MaterialDesignIconName
|
|
: T extends 'entypo'
|
|
? EntypoIconName
|
|
: never;
|
|
|
|
export interface IconProps<T extends IconType = IconType> {
|
|
name: IconNameFor<T>;
|
|
type?: T;
|
|
/**
|
|
* @default 24
|
|
*/
|
|
size?: number;
|
|
color?: string;
|
|
style?: StyleProp<TextStyle>;
|
|
iconStyle?: T extends 'font-awesome-6' ? 'solid' | 'brand' | 'regular' : StyleProp<TextStyle>;
|
|
containerStyle?: StyleProp<ViewStyle>;
|
|
onPress?: () => void;
|
|
accessibilityLabel?: string;
|
|
testID?: string;
|
|
}
|
|
|
|
const ICON_COMPONENTS: Record<IconType, IconComponentType> = {
|
|
'font-awesome': FontAwesome,
|
|
'font-awesome-6': FontAwesome6,
|
|
ionicons: Ionicons,
|
|
material: MaterialIcons,
|
|
'material-community': MaterialDesignIcons,
|
|
entypo: Entypo,
|
|
};
|
|
|
|
const Icon = <T extends IconType = 'font-awesome'>({
|
|
name,
|
|
type,
|
|
size = 24,
|
|
color,
|
|
style,
|
|
iconStyle,
|
|
containerStyle,
|
|
onPress,
|
|
accessibilityLabel,
|
|
testID,
|
|
}: IconProps<T>): React.ReactElement | null => {
|
|
const IconComponent = ICON_COMPONENTS[type ?? 'font-awesome'] as React.ComponentType<any>;
|
|
const isFa6 = type === 'font-awesome-6';
|
|
const fa6IconStyle = isFa6 ? (typeof iconStyle === 'string' ? iconStyle : 'solid') : undefined;
|
|
const mergedStyle = isFa6 ? style : [style, iconStyle];
|
|
|
|
const content = (
|
|
<IconComponent
|
|
name={name}
|
|
size={size}
|
|
color={color}
|
|
style={mergedStyle}
|
|
iconStyle={fa6IconStyle}
|
|
accessibilityLabel={accessibilityLabel}
|
|
testID={testID}
|
|
/>
|
|
);
|
|
|
|
if (onPress) {
|
|
return (
|
|
<Pressable accessibilityRole="imagebutton" onPress={onPress} style={containerStyle}>
|
|
{content}
|
|
</Pressable>
|
|
);
|
|
}
|
|
|
|
if (containerStyle) {
|
|
return <View style={containerStyle}>{content}</View>;
|
|
}
|
|
|
|
return content;
|
|
};
|
|
|
|
export default Icon;
|