320 lines
8.0 KiB
TypeScript
320 lines
8.0 KiB
TypeScript
import React, { useState } from "react";
|
|
import {
|
|
View,
|
|
Text,
|
|
TouchableOpacity,
|
|
StyleSheet,
|
|
Modal,
|
|
Platform,
|
|
ScrollView,
|
|
TextInput,
|
|
} from "react-native";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import { useThings } from "@/state/use-thing";
|
|
import { useI18n } from "@/hooks/use-i18n";
|
|
|
|
interface ShipOption {
|
|
id: string;
|
|
shipName: string;
|
|
}
|
|
|
|
interface ShipDropdownProps {
|
|
value: ShipOption | null;
|
|
onChange: (value: ShipOption | null) => void;
|
|
}
|
|
|
|
export default function ShipDropdown({ value, onChange }: ShipDropdownProps) {
|
|
const { t } = useI18n();
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const [searchText, setSearchText] = useState("");
|
|
|
|
const { things } = useThings();
|
|
|
|
// Convert things to ship options, filter out items without id
|
|
const shipOptions: ShipOption[] =
|
|
things
|
|
?.filter((thing) => thing.id != null)
|
|
.map((thing) => ({
|
|
id: thing.id as string,
|
|
shipName: thing.metadata?.ship_name || "",
|
|
})) || [];
|
|
|
|
// Filter ships based on search text
|
|
const filteredShips = shipOptions.filter((ship) => {
|
|
const searchLower = searchText.toLowerCase();
|
|
return ship.shipName.toLowerCase().includes(searchLower);
|
|
});
|
|
|
|
const handleSelect = (ship: ShipOption | null) => {
|
|
onChange(ship);
|
|
setIsOpen(false);
|
|
setSearchText("");
|
|
};
|
|
|
|
const displayValue = value ? value.shipName : t("diary.shipDropdown.placeholder");
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<Text style={styles.label}>{t("diary.shipDropdown.label")}</Text>
|
|
<TouchableOpacity
|
|
style={styles.selector}
|
|
onPress={() => setIsOpen(true)}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Text style={[styles.selectorText, !value && styles.placeholder]}>
|
|
{displayValue}
|
|
</Text>
|
|
<Ionicons name="chevron-down" size={20} color="#6B7280" />
|
|
</TouchableOpacity>
|
|
|
|
<Modal
|
|
visible={isOpen}
|
|
transparent
|
|
animationType="fade"
|
|
onRequestClose={() => setIsOpen(false)}
|
|
>
|
|
<TouchableOpacity
|
|
style={styles.modalOverlay}
|
|
activeOpacity={1}
|
|
onPress={() => setIsOpen(false)}
|
|
>
|
|
<View
|
|
style={styles.modalContent}
|
|
onStartShouldSetResponder={() => true}
|
|
>
|
|
{/* Search Input */}
|
|
<View style={styles.searchContainer}>
|
|
<Ionicons
|
|
name="search"
|
|
size={20}
|
|
color="#9CA3AF"
|
|
style={styles.searchIcon}
|
|
/>
|
|
<TextInput
|
|
style={styles.searchInput}
|
|
placeholder={t("diary.shipDropdown.searchPlaceholder")}
|
|
placeholderTextColor="#9CA3AF"
|
|
value={searchText}
|
|
onChangeText={setSearchText}
|
|
autoCapitalize="none"
|
|
/>
|
|
{searchText.length > 0 && (
|
|
<TouchableOpacity onPress={() => setSearchText("")}>
|
|
<Ionicons name="close-circle" size={20} color="#9CA3AF" />
|
|
</TouchableOpacity>
|
|
)}
|
|
</View>
|
|
|
|
<ScrollView style={styles.optionsList}>
|
|
{/* Option to clear selection */}
|
|
<TouchableOpacity
|
|
style={[styles.option, !value && styles.selectedOption]}
|
|
onPress={() => handleSelect(null)}
|
|
>
|
|
<Text
|
|
style={[
|
|
styles.optionText,
|
|
styles.placeholderOption,
|
|
!value && styles.selectedOptionText,
|
|
]}
|
|
>
|
|
{t("diary.shipDropdown.allShips")}
|
|
</Text>
|
|
{!value && (
|
|
<Ionicons name="checkmark" size={20} color="#3B82F6" />
|
|
)}
|
|
</TouchableOpacity>
|
|
|
|
{filteredShips.map((ship) => (
|
|
<TouchableOpacity
|
|
key={ship.id}
|
|
style={[
|
|
styles.option,
|
|
value?.id === ship.id && styles.selectedOption,
|
|
]}
|
|
onPress={() => handleSelect(ship)}
|
|
>
|
|
<View style={styles.shipInfo}>
|
|
<Text
|
|
style={[
|
|
styles.shipName,
|
|
value?.id === ship.id && styles.selectedOptionText,
|
|
]}
|
|
>
|
|
{ship.shipName}
|
|
</Text>
|
|
</View>
|
|
{value?.id === ship.id && (
|
|
<Ionicons name="checkmark" size={20} color="#3B82F6" />
|
|
)}
|
|
</TouchableOpacity>
|
|
))}
|
|
|
|
{filteredShips.length === 0 && searchText.length > 0 && (
|
|
<View style={styles.emptyState}>
|
|
<Text style={styles.emptyText}>
|
|
{t("diary.shipDropdown.noShipsFound")}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</ScrollView>
|
|
</View>
|
|
</TouchableOpacity>
|
|
</Modal>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
marginBottom: 20,
|
|
},
|
|
label: {
|
|
fontSize: 16,
|
|
fontWeight: "600",
|
|
color: "#111827",
|
|
marginBottom: 8,
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
selector: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
backgroundColor: "#FFFFFF",
|
|
borderWidth: 1,
|
|
borderColor: "#D1D5DB",
|
|
borderRadius: 8,
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 12,
|
|
},
|
|
selectorText: {
|
|
fontSize: 16,
|
|
color: "#111827",
|
|
flex: 1,
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
placeholder: {
|
|
color: "#9CA3AF",
|
|
},
|
|
modalOverlay: {
|
|
flex: 1,
|
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
},
|
|
modalContent: {
|
|
backgroundColor: "#FFFFFF",
|
|
borderRadius: 12,
|
|
width: "85%",
|
|
maxHeight: "70%",
|
|
overflow: "hidden",
|
|
shadowColor: "#000",
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 4,
|
|
},
|
|
shadowOpacity: 0.3,
|
|
shadowRadius: 8,
|
|
elevation: 8,
|
|
},
|
|
searchContainer: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 12,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: "#F3F4F6",
|
|
backgroundColor: "#F9FAFB",
|
|
},
|
|
searchIcon: {
|
|
marginRight: 8,
|
|
},
|
|
searchInput: {
|
|
flex: 1,
|
|
fontSize: 16,
|
|
color: "#111827",
|
|
padding: 0,
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
optionsList: {
|
|
maxHeight: 350,
|
|
},
|
|
option: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
paddingHorizontal: 20,
|
|
paddingVertical: 16,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: "#F3F4F6",
|
|
},
|
|
selectedOption: {
|
|
backgroundColor: "#EFF6FF",
|
|
},
|
|
optionText: {
|
|
fontSize: 16,
|
|
color: "#111827",
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
placeholderOption: {
|
|
fontStyle: "italic",
|
|
color: "#6B7280",
|
|
},
|
|
selectedOptionText: {
|
|
color: "#3B82F6",
|
|
fontWeight: "600",
|
|
},
|
|
shipInfo: {
|
|
flex: 1,
|
|
},
|
|
shipName: {
|
|
fontSize: 16,
|
|
color: "#111827",
|
|
fontWeight: "500",
|
|
marginBottom: 2,
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
regNumber: {
|
|
fontSize: 14,
|
|
color: "#6B7280",
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
emptyState: {
|
|
paddingVertical: 24,
|
|
alignItems: "center",
|
|
},
|
|
emptyText: {
|
|
fontSize: 14,
|
|
color: "#9CA3AF",
|
|
fontFamily: Platform.select({
|
|
ios: "System",
|
|
android: "Roboto",
|
|
default: "System",
|
|
}),
|
|
},
|
|
});
|