Component

Add:
- Badge
- Container

Voting
Add:
- screens/Voting

Fix:
- (tabs)/ index, contribution, status

# No Issue
This commit is contained in:
2025-07-28 12:24:35 +08:00
parent 20258d1fe5
commit b18044f53f
10 changed files with 467 additions and 19 deletions

View File

@@ -0,0 +1,189 @@
import React from "react";
import {
StyleSheet,
Text,
TextStyle,
View,
ViewProps,
ViewStyle,
} from "react-native";
type BadgeVariant = "filled" | "light" | "outline" | "dot";
type BadgeColor =
| "primary"
| "success"
| "warning"
| "danger"
| "gray"
| "dark";
type BadgeSize = "xs" | "sm" | "md" | "lg";
interface BadgeProps extends ViewProps {
children: React.ReactNode;
variant?: BadgeVariant;
color?: BadgeColor;
size?: BadgeSize;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
radius?: number;
fullWidth?: boolean;
textColor?: string;
}
const BadgeCustom: React.FC<BadgeProps> = ({
children,
variant = "filled",
color = "primary",
size = "md",
leftIcon,
rightIcon,
radius = 50,
fullWidth = false,
textColor = "#fff",
style,
...props
}) => {
const colors = {
primary: "#339AF0",
success: "#40C057",
warning: "#FAB005",
danger: "#FA5252",
gray: "#868E96",
dark: "#212529",
};
const themeColor = colors[color];
// Ganti bagian sizeStyles dan styles.container
const sizeStyles = {
xs: {
fontSize: 10,
paddingHorizontal: 6,
paddingVertical: 2,
height: 18, // Dinaikkan dari 16 → 18 agar teks tidak terpotong
lineHeight: 10, // 👈 Penting: match fontSize agar kontrol vertikal lebih baik
},
sm: {
fontSize: 11,
paddingHorizontal: 8,
paddingVertical: 3,
height: 20,
lineHeight: 11,
},
md: {
fontSize: 12,
paddingHorizontal: 10,
paddingVertical: 4,
height: 24,
lineHeight: 12,
},
lg: {
fontSize: 14,
paddingHorizontal: 12,
paddingVertical: 6,
height: 30,
lineHeight: 14,
},
};
const currentSize = sizeStyles[size];
let variantStyles: ViewStyle & { text: TextStyle } = {
backgroundColor: themeColor,
borderColor: themeColor,
borderWidth: 1,
borderRadius: radius,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
text: { color: textColor, fontWeight: "600" },
};
switch (variant) {
case "light":
variantStyles.backgroundColor = `${themeColor}20`;
variantStyles.text.color = themeColor;
break;
case "outline":
variantStyles.backgroundColor = "transparent";
variantStyles.text.color = themeColor;
break;
case "dot":
variantStyles.backgroundColor = themeColor;
variantStyles.paddingHorizontal = 0;
variantStyles.paddingVertical = 0;
variantStyles.height = currentSize.fontSize * 2;
variantStyles.width = currentSize.fontSize * 2;
variantStyles.borderRadius = currentSize.fontSize;
break;
default:
break;
}
if (variant === "dot") {
return (
<View
style={[variantStyles, fullWidth && styles.fullWidth, style]}
{...props}
/>
);
}
return (
<View
style={[
styles.container,
variantStyles,
currentSize,
{ borderRadius: radius },
fullWidth && styles.fullWidth,
style,
]}
{...props}
>
{leftIcon && <View style={styles.iconContainer}>{leftIcon}</View>}
<Text
style={[
styles.text,
variantStyles.text,
{ fontSize: currentSize.fontSize },
]}
>
{children}
</Text>
{rightIcon && <View style={styles.iconContainer}>{rightIcon}</View>}
</View>
);
};
const styles = StyleSheet.create({
container: {
alignSelf: "flex-start",
flexDirection: "row",
alignItems: "center", // Vertikal center anak-anak (termasuk teks)
justifyContent: "center", // Horizontal center
paddingHorizontal: 10,
paddingVertical: 4,
minWidth: 20,
borderRadius: 6,
// ❌ Jangan gunakan `height` fix di sini — kita override per size
},
text: {
fontWeight: "600",
textAlign: "center",
// ❌ Hapus marginHorizontal jika mengganggu alignment
// marginHorizontal: 2, // Opsional, bisa dihapus atau dikurangi
includeFontPadding: false, // 👈 Ini penting untuk Android!
padding: 0, // Bersihkan padding tambahan dari font
},
iconContainer: {
marginHorizontal: 2, // Lebih kecil dari sebelumnya agar tidak ganggu ukuran kecil
},
fullWidth: {
width: "100%",
alignSelf: "stretch",
justifyContent: "center",
},
});
export default BadgeCustom;

View File

@@ -0,0 +1,44 @@
import { MainColor } from "@/constants/color-palet";
import React from "react";
import { StyleSheet, TextInput, View } from "react-native";
interface CircularInputProps {
value: string | number;
onChange?: (value: string) => void;
}
const CircularInput: React.FC<CircularInputProps> = ({ value, onChange }) => {
return (
<View style={styles.circleContainer}>
<TextInput
value={String(value)}
onChangeText={onChange}
style={styles.input}
keyboardType="numeric"
maxLength={2} // Batasan maksimal karakter
/>
</View>
);
};
const styles = StyleSheet.create({
circleContainer: {
width: 60,
height: 60,
borderRadius: 40, // Setiap setengah dari lebar/tinggi
borderWidth: 2,
borderColor: MainColor.yellow, // Warna kuning
justifyContent: "center",
alignItems: "center",
},
input: {
color: MainColor.yellow, // Warna kuning
fontSize: 24,
fontWeight: "bold",
textAlign: "center",
padding: 0,
backgroundColor: "transparent",
},
});
export default CircularInput;

View File

@@ -7,6 +7,10 @@ import ButtonCenteredOnly from "./Button/ButtonCenteredOnly";
import ButtonCustom from "./Button/ButtonCustom";
import DotButton from "./Button/DotButton";
import FloatingButton from "./Button/FloatingButton";
// Badge
import BadgeCustom from "./Badge/BadgeCustom";
// Container
import CircleContainer from "./Container/CircleContainer";
// Checkbox
import CheckboxCustom from "./Checkbox/CheckboxCustom";
import CheckboxGroup from "./Checkbox/CheckboxGroup";
@@ -68,6 +72,8 @@ export {
ButtonCenteredOnly,
ButtonCustom,
DotButton,
// Badge
BadgeCustom,
// Center
CenterCustom,
// Checkbox
@@ -75,6 +81,8 @@ export {
CheckboxGroup,
// Clickable
ClickableCustom,
// Container
CircleContainer,
// Divider
Divider,
DividerCustom,