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:
@@ -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 };
|
||||
|
||||
75
components/Checkbox/CheckboxGroup.tsx
Normal file
75
components/Checkbox/CheckboxGroup.tsx
Normal 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;
|
||||
|
||||
@@ -11,8 +11,7 @@ export const checkboxStyles = (props: {
|
||||
container: {
|
||||
flexDirection: "row",
|
||||
alignItems: "flex-start",
|
||||
// marginBottom: 12,
|
||||
|
||||
// marginBottom: 12,
|
||||
},
|
||||
innerContainer: {
|
||||
flexDirection: "row",
|
||||
|
||||
10
components/_Icon/IconEdit.tsx
Normal file
10
components/_Icon/IconEdit.tsx
Normal 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" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
3
components/_Icon/index.ts
Normal file
3
components/_Icon/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import IconEdit from "./IconEdit";
|
||||
|
||||
export { IconEdit };
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user