160 lines
4.1 KiB
TypeScript
160 lines
4.1 KiB
TypeScript
import React from "react";
|
|
import {
|
|
ActivityIndicator,
|
|
GestureResponderEvent,
|
|
StyleProp,
|
|
StyleSheet,
|
|
Text,
|
|
TouchableOpacity,
|
|
View,
|
|
ViewStyle,
|
|
} from "react-native";
|
|
|
|
type ButtonType = "primary" | "default" | "dashed" | "text" | "link" | "danger";
|
|
type ButtonShape = "default" | "circle" | "round";
|
|
type ButtonSize = "small" | "middle" | "large";
|
|
|
|
export interface IconButtonProps {
|
|
type?: ButtonType;
|
|
shape?: ButtonShape;
|
|
size?: ButtonSize;
|
|
icon?: React.ReactNode; // render an icon component, e.g. <AntDesign name="plus" />
|
|
loading?: boolean;
|
|
disabled?: boolean;
|
|
onPress?: (e?: GestureResponderEvent) => void;
|
|
children?: React.ReactNode; // label text
|
|
style?: StyleProp<ViewStyle>;
|
|
block?: boolean; // full width
|
|
activeOpacity?: number;
|
|
}
|
|
|
|
/**
|
|
* IconButton
|
|
* A lightweight Button component inspired by Ant Design Button API, tuned for React Native.
|
|
* Accepts an `icon` prop as a React node for maximum flexibility.
|
|
*/
|
|
const IconButton: React.FC<IconButtonProps> = ({
|
|
type = "default",
|
|
shape = "default",
|
|
size = "middle",
|
|
icon,
|
|
loading = false,
|
|
disabled = false,
|
|
onPress,
|
|
children,
|
|
style,
|
|
block = false,
|
|
activeOpacity = 0.8,
|
|
}) => {
|
|
const sizeMap = {
|
|
small: { height: 32, fontSize: 14, paddingHorizontal: 10 },
|
|
middle: { height: 40, fontSize: 16, paddingHorizontal: 14 },
|
|
large: { height: 48, fontSize: 18, paddingHorizontal: 18 },
|
|
} as const;
|
|
|
|
const colors: Record<
|
|
ButtonType,
|
|
{ backgroundColor?: string; textColor: string; borderColor?: string }
|
|
> = {
|
|
primary: { backgroundColor: "#4ecdc4", textColor: "#fff" },
|
|
default: {
|
|
backgroundColor: "#f2f2f2",
|
|
textColor: "#111",
|
|
borderColor: "#e6e6e6",
|
|
},
|
|
dashed: {
|
|
backgroundColor: "#fff",
|
|
textColor: "#111",
|
|
borderColor: "#d9d9d9",
|
|
},
|
|
text: { backgroundColor: "transparent", textColor: "#111" },
|
|
link: { backgroundColor: "transparent", textColor: "#4ecdc4" },
|
|
danger: { backgroundColor: "#e74c3c", textColor: "#fff" },
|
|
};
|
|
|
|
const sz = sizeMap[size];
|
|
const color = colors[type];
|
|
|
|
const isCircle = shape === "circle";
|
|
const isRound = shape === "round";
|
|
|
|
const handlePress = (e: GestureResponderEvent) => {
|
|
if (disabled || loading) return;
|
|
onPress?.(e);
|
|
};
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
activeOpacity={activeOpacity}
|
|
onPress={handlePress}
|
|
disabled={disabled || loading}
|
|
style={[
|
|
styles.button,
|
|
{
|
|
height: sz.height,
|
|
paddingHorizontal: isCircle ? 0 : sz.paddingHorizontal,
|
|
backgroundColor: color.backgroundColor ?? "transparent",
|
|
borderColor: color.borderColor ?? "transparent",
|
|
borderWidth: type === "dashed" ? 1 : color.borderColor ? 1 : 0,
|
|
width: isCircle ? sz.height : block ? "100%" : undefined,
|
|
borderRadius: isCircle ? sz.height / 2 : isRound ? 999 : 8,
|
|
opacity: disabled ? 0.6 : 1,
|
|
},
|
|
type === "dashed" ? { borderStyle: "dashed" } : null,
|
|
style,
|
|
]}
|
|
>
|
|
<View style={styles.content}>
|
|
{loading ? (
|
|
<ActivityIndicator
|
|
size={"small"}
|
|
color={color.textColor}
|
|
style={styles.iconContainer}
|
|
/>
|
|
) : icon ? (
|
|
<View style={styles.iconContainer}>{icon}</View>
|
|
) : null}
|
|
|
|
{children ? (
|
|
<Text
|
|
numberOfLines={1}
|
|
style={[
|
|
styles.text,
|
|
{
|
|
color: color.textColor,
|
|
fontSize: sz.fontSize,
|
|
marginLeft: icon || loading ? 6 : 0,
|
|
},
|
|
]}
|
|
>
|
|
{children}
|
|
</Text>
|
|
) : null}
|
|
</View>
|
|
</TouchableOpacity>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
button: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
borderRadius: 8,
|
|
borderColor: "transparent",
|
|
},
|
|
content: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
},
|
|
iconContainer: {
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
},
|
|
text: {
|
|
fontWeight: "600",
|
|
},
|
|
});
|
|
|
|
export default IconButton;
|