Component

Add :
- CheckboxGroup
- components/_Icon/

Fix:
- CheckboxCustom: penambahan fitur untuk checkbox group

Feature:
Collaboration
Add :
- ProjectMainSelectedSection

Fix :
- detail-participant
- detail-project-main

# No Issue
This commit is contained in:
2025-07-24 16:47:16 +08:00
parent 4f8ae2d7e0
commit e02ae8e35d
11 changed files with 346 additions and 93 deletions

View File

@@ -1,61 +1,82 @@
import React, { useState } from "react";
import { View, Text, TouchableOpacity, Animated, Easing } from "react-native";
import { AccentColor } from "@/constants/color-palet";
import { MaterialIcons } from "@expo/vector-icons"; // Bisa diganti dengan ikon lain
import React, { useContext } from "react";
import { Animated, Text, TouchableOpacity, View } from "react-native";
import { checkboxStyles } from "./checkbox-styles";
// Context untuk Group
interface CheckboxGroupContextType {
value: (string | number)[];
onChange: (value: (string | number)[]) => void;
disabled?: boolean;
}
const CheckboxGroupContext =
React.createContext<CheckboxGroupContextType | null>(null);
// Tipe props
// Tambahkan prop baru: groupValueKey
interface CheckboxProps {
label?: string;
description?: string;
error?: string;
value?: boolean;
value?: boolean; // controlled value (untuk standalone)
onChange?: (checked: boolean) => void;
disabled?: boolean;
size?: number; // ukuran checkbox (default: 20)
color?: string; // warna utama (default: '#3b82f6' - biru tailwind)
size?: number;
color?: string;
style?: object;
component?: React.ReactNode;
// Prop tambahan untuk Group
valueKey?: string | number; // nilai unik untuk identifikasi di group
}
export const CheckboxCustom: React.FC<CheckboxProps> = ({
const CheckboxCustom: React.FC<CheckboxProps> = ({
label,
description,
error,
value: controlledValue,
onChange,
disabled = false,
disabled: propDisabled,
size = 20,
color = "#3b82f6",
color = AccentColor.softblue,
style,
component,
valueKey,
}) => {
const [uncontrolledChecked, setUncontrolledChecked] = useState(false);
const isChecked = controlledValue ?? uncontrolledChecked;
// const [uncontrolledChecked, setUncontrolledChecked] = useState(false);
// const isChecked = controlledValue ?? uncontrolledChecked;
// const scaleValue = new Animated.Value(isChecked ? 1 : 0);
const group = useContext(CheckboxGroupContext);
const isInsideGroup = !!group && valueKey !== undefined;
// Jika di dalam group, gunakan logika group
const isChecked = isInsideGroup
? group.value.includes(valueKey!)
: controlledValue ?? false;
const disabled = propDisabled || (isInsideGroup && group.disabled);
const scaleValue = new Animated.Value(isChecked ? 1 : 0);
const toggle = () => {
if (disabled) return;
const newValue = !isChecked;
if (onChange) onChange(newValue);
if (controlledValue === undefined) {
setUncontrolledChecked(newValue);
if (isInsideGroup) {
const newValue = isChecked
? group.value.filter((v) => v !== valueKey)
: [...group.value, valueKey!];
group.onChange(newValue);
} else if (onChange) {
onChange(!controlledValue);
}
// Animasi scale
Animated.spring(scaleValue, {
toValue: newValue ? 1 : 0,
friction: 7,
tension: 40,
useNativeDriver: true,
}).start();
};
const styles = checkboxStyles({
size,
color,
disabled,
disabled: disabled as boolean,
error: !!error,
});
@@ -106,4 +127,7 @@ export const CheckboxCustom: React.FC<CheckboxProps> = ({
);
};
export default CheckboxCustom
export default CheckboxCustom;
// Export context agar bisa digunakan
export { CheckboxGroupContext };

View File

@@ -0,0 +1,75 @@
import React, { useState, useMemo } from "react";
import { View, Text, StyleSheet } from "react-native";
import { CheckboxGroupContext } from "./CheckboxCustom";
interface CheckboxGroupProps {
value?: (string | number)[];
onChange?: (values: (string | number)[]) => void;
defaultValue?: (string | number)[];
label?: string;
description?: string;
error?: string;
disabled?: boolean;
children: React.ReactNode;
style?: object;
}
const CheckboxGroup: React.FC<CheckboxGroupProps> = ({
value: controlledValue,
onChange,
defaultValue = [],
label,
description,
error,
disabled = false,
children,
style,
}) => {
const [uncontrolledValue, setUncontrolledValue] =
useState<(string | number)[]>(defaultValue);
const value = controlledValue ?? uncontrolledValue;
const handleChange = onChange ?? setUncontrolledValue;
const contextValue = useMemo(
() => ({ value, onChange: handleChange, disabled }),
[value, handleChange, disabled]
);
return (
<CheckboxGroupContext.Provider value={contextValue}>
<View style={[styles.container, style]}>
{label ? <Text style={styles.label}>{label}</Text> : null}
{description ? (
<Text style={styles.description}>{description}</Text>
) : null}
{children}
{error ? <Text style={styles.errorText}>{error}</Text> : null}
</View>
</CheckboxGroupContext.Provider>
);
};
const styles = StyleSheet.create({
container: {
gap: 8,
},
label: {
fontSize: 16,
fontWeight: "600",
color: "#f8f9fa",
marginBottom: 4,
},
description: {
fontSize: 14,
color: "#ced4da",
marginBottom: 8,
},
errorText: {
color: "#e03131",
fontSize: 14,
marginTop: 4,
},
});
export default CheckboxGroup;

View File

@@ -11,8 +11,7 @@ export const checkboxStyles = (props: {
container: {
flexDirection: "row",
alignItems: "flex-start",
// marginBottom: 12,
// marginBottom: 12,
},
innerContainer: {
flexDirection: "row",

View File

@@ -0,0 +1,10 @@
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import { FontAwesome5 } from "@expo/vector-icons";
export default function IconEdit() {
return (
<>
<FontAwesome5 name="edit" size={ICON_SIZE_SMALL} color="white" />
</>
);
}

View File

@@ -0,0 +1,3 @@
import IconEdit from "./IconEdit";
export { IconEdit };

View File

@@ -7,6 +7,9 @@ import ButtonCenteredOnly from "./Button/ButtonCenteredOnly";
import ButtonCustom from "./Button/ButtonCustom";
import DotButton from "./Button/DotButton";
import FloatingButton from "./Button/FloatingButton";
// Checkbox
import CheckboxCustom from "./Checkbox/CheckboxCustom";
import CheckboxGroup from "./Checkbox/CheckboxGroup";
// Drawer
import DrawerCustom from "./Drawer/DrawerCustom";
import MenuDrawerDynamicGrid from "./Drawer/MenuDrawerDynamicGird";
@@ -59,18 +62,23 @@ export {
// Box
BaseBox,
BoxButtonOnFooter,
BoxWithHeaderSection, ButtonCenteredOnly,
BoxWithHeaderSection,
ButtonCenteredOnly,
ButtonCustom,
DotButton,
// Center
CenterCustom,
// Checkbox
CheckboxCustom,
CheckboxGroup,
// Clickable
ClickableCustom,
// Divider
Divider,
DividerCustom,
DividerCustom,
// Drawer
DrawerCustom, FloatingButton,
DrawerCustom,
FloatingButton,
// Grid
Grid,
InformationBox,
@@ -85,7 +93,8 @@ export {
// ShareComponent
Spacing,
// Stack
StackCustom, TabBarBackground,
StackCustom,
TabBarBackground,
// TextArea
TextAreaCustom,
// Text
@@ -93,6 +102,5 @@ export {
// TextInput
TextInputCustom,
// ViewWrapper
ViewWrapper
ViewWrapper,
};