thêm FormSearch trong map
This commit is contained in:
@@ -20,16 +20,16 @@ export interface SelectOption {
|
||||
}
|
||||
|
||||
export interface SelectProps {
|
||||
value?: string | number;
|
||||
defaultValue?: string | number;
|
||||
value?: string | number | (string | number)[];
|
||||
defaultValue?: string | number | (string | number)[];
|
||||
options: SelectOption[];
|
||||
onChange?: (value: string | number | undefined) => void;
|
||||
onChange?: (value: string | number | (string | number)[] | undefined) => void;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
allowClear?: boolean;
|
||||
showSearch?: boolean;
|
||||
mode?: "single" | "multiple"; // multiple not implemented yet
|
||||
mode?: "single" | "multiple";
|
||||
style?: StyleProp<ViewStyle>;
|
||||
size?: "small" | "middle" | "large";
|
||||
listStyle?: StyleProp<ViewStyle>;
|
||||
@@ -38,7 +38,7 @@ export interface SelectProps {
|
||||
/**
|
||||
* Select
|
||||
* A Select component inspired by Ant Design, adapted for React Native.
|
||||
* Supports single selection, search, clear, loading, disabled states.
|
||||
* Supports single and multiple selection, search, clear, loading, disabled states.
|
||||
*/
|
||||
const Select: React.FC<SelectProps> = ({
|
||||
value,
|
||||
@@ -55,16 +55,23 @@ const Select: React.FC<SelectProps> = ({
|
||||
listStyle,
|
||||
size = "middle",
|
||||
}) => {
|
||||
const [selectedValue, setSelectedValue] = useState<
|
||||
string | number | undefined
|
||||
>(value ?? defaultValue);
|
||||
const initialValue = value ?? defaultValue;
|
||||
const [selectedValues, setSelectedValues] = useState<(string | number)[]>(
|
||||
Array.isArray(initialValue)
|
||||
? initialValue
|
||||
: initialValue
|
||||
? [initialValue]
|
||||
: []
|
||||
);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [containerHeight, setContainerHeight] = useState(0);
|
||||
const [textHeight, setTextHeight] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedValue(value);
|
||||
}, [value]);
|
||||
const newVal = value ?? defaultValue;
|
||||
setSelectedValues(Array.isArray(newVal) ? newVal : newVal ? [newVal] : []);
|
||||
}, [value, defaultValue]);
|
||||
|
||||
const filteredOptions = showSearch
|
||||
? options.filter((opt) =>
|
||||
@@ -72,17 +79,31 @@ const Select: React.FC<SelectProps> = ({
|
||||
)
|
||||
: options;
|
||||
|
||||
const selectedOption = options.find((opt) => opt.value === selectedValue);
|
||||
|
||||
const handleSelect = (val: string | number) => {
|
||||
setSelectedValue(val);
|
||||
onChange?.(val);
|
||||
setIsOpen(false);
|
||||
setSearchText("");
|
||||
let newSelected: (string | number)[];
|
||||
if (mode === "single") {
|
||||
newSelected = [val];
|
||||
} else {
|
||||
newSelected = selectedValues.includes(val)
|
||||
? selectedValues.filter((v) => v !== val)
|
||||
: [...selectedValues, val];
|
||||
}
|
||||
setSelectedValues(newSelected);
|
||||
onChange?.(
|
||||
mode === "single"
|
||||
? newSelected.length > 0
|
||||
? newSelected[0]
|
||||
: undefined
|
||||
: newSelected
|
||||
);
|
||||
if (mode === "single") {
|
||||
setIsOpen(false);
|
||||
setSearchText("");
|
||||
}
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
setSelectedValue(undefined);
|
||||
setSelectedValues([]);
|
||||
onChange?.(undefined);
|
||||
};
|
||||
|
||||
@@ -100,13 +121,26 @@ const Select: React.FC<SelectProps> = ({
|
||||
? colors.backgroundSecondary
|
||||
: colors.surface;
|
||||
|
||||
let displayText = placeholder;
|
||||
if (selectedValues.length > 0) {
|
||||
if (mode === "single") {
|
||||
const opt = options.find((o) => o.value === selectedValues[0]);
|
||||
displayText = opt?.label || placeholder;
|
||||
} else {
|
||||
const labels = selectedValues
|
||||
.map((v) => options.find((o) => o.value === v)?.label)
|
||||
.filter(Boolean);
|
||||
displayText = labels.join(", ");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.wrapper}>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.container,
|
||||
{
|
||||
height: sz.height,
|
||||
height: Math.max(sz.height, textHeight + 16), // Add padding
|
||||
paddingHorizontal: sz.paddingHorizontal,
|
||||
backgroundColor: selectBackgroundColor,
|
||||
borderColor: disabled ? colors.border : colors.primary,
|
||||
@@ -129,19 +163,19 @@ const Select: React.FC<SelectProps> = ({
|
||||
fontSize: sz.fontSize,
|
||||
color: disabled
|
||||
? colors.textSecondary
|
||||
: selectedValue
|
||||
: selectedValues.length > 0
|
||||
? colors.text
|
||||
: colors.textSecondary,
|
||||
},
|
||||
]}
|
||||
numberOfLines={1}
|
||||
onLayout={(e) => setTextHeight(e.nativeEvent.layout.height)}
|
||||
>
|
||||
{selectedOption?.label || placeholder}
|
||||
{displayText}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.suffix}>
|
||||
{allowClear && selectedValue && !loading ? (
|
||||
{allowClear && selectedValues.length > 0 && !loading ? (
|
||||
<TouchableOpacity onPress={handleClear} style={styles.icon}>
|
||||
<AntDesign name="close" size={16} color={colors.textSecondary} />
|
||||
</TouchableOpacity>
|
||||
@@ -193,7 +227,7 @@ const Select: React.FC<SelectProps> = ({
|
||||
borderBottomColor: colors.separator,
|
||||
},
|
||||
item.disabled && styles.optionDisabled,
|
||||
selectedValue === item.value && {
|
||||
selectedValues.includes(item.value) && {
|
||||
backgroundColor: colors.primary + "20", // Add transparency to primary color
|
||||
},
|
||||
]}
|
||||
@@ -209,7 +243,7 @@ const Select: React.FC<SelectProps> = ({
|
||||
item.disabled && {
|
||||
color: colors.textSecondary,
|
||||
},
|
||||
selectedValue === item.value && {
|
||||
selectedValues.includes(item.value) && {
|
||||
color: colors.primary,
|
||||
fontWeight: "600",
|
||||
},
|
||||
@@ -217,7 +251,7 @@ const Select: React.FC<SelectProps> = ({
|
||||
>
|
||||
{item.label}
|
||||
</Text>
|
||||
{selectedValue === item.value && (
|
||||
{selectedValues.includes(item.value) && (
|
||||
<AntDesign name="check" size={16} color={colors.primary} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
|
||||
Reference in New Issue
Block a user