179 lines
5.1 KiB
TypeScript
179 lines
5.1 KiB
TypeScript
import DevicesScreen from "@/components/manager/devices";
|
|
import FleetsScreen from "@/components/manager/fleets";
|
|
import ShipsScreen from "@/components/manager/ships";
|
|
import { ThemedText } from "@/components/themed-text";
|
|
import { ThemedView } from "@/components/themed-view";
|
|
import { Colors } from "@/config";
|
|
import { ColorScheme, useTheme } from "@/hooks/use-theme-context";
|
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
import { Animated, StyleSheet, TouchableOpacity, View } from "react-native";
|
|
import { SafeAreaView } from "react-native-safe-area-context";
|
|
|
|
export default function manager() {
|
|
const { colors, colorScheme } = useTheme();
|
|
const styles = useMemo(
|
|
() => createStyles(colors, colorScheme),
|
|
[colors, colorScheme]
|
|
);
|
|
|
|
const [selected, setSelected] = useState<"ships" | "devices" | "fleets">(
|
|
"ships"
|
|
);
|
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
const indicatorTranslate = useRef(new Animated.Value(0)).current;
|
|
const SEGMENT_COUNT = 3;
|
|
const indexMap: Record<string, number> = {
|
|
ships: 0,
|
|
devices: 1,
|
|
fleets: 2,
|
|
};
|
|
|
|
const SegmentButton = ({
|
|
label,
|
|
active,
|
|
onPress,
|
|
}: {
|
|
label: string;
|
|
active?: boolean;
|
|
onPress?: () => void;
|
|
}) => {
|
|
return (
|
|
<TouchableOpacity
|
|
style={[styles.segmentButton, active && styles.segmentButtonActive]}
|
|
onPress={onPress}
|
|
activeOpacity={0.8}
|
|
>
|
|
<ThemedText
|
|
style={[styles.segmentText, active && styles.segmentTextActive]}
|
|
>
|
|
{label}
|
|
</ThemedText>
|
|
</TouchableOpacity>
|
|
);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (containerWidth <= 0) return;
|
|
const segmentWidth = containerWidth / SEGMENT_COUNT;
|
|
const toValue = indexMap[selected] * segmentWidth;
|
|
Animated.spring(indicatorTranslate, {
|
|
toValue,
|
|
useNativeDriver: true,
|
|
friction: 14,
|
|
tension: 100,
|
|
}).start();
|
|
}, [selected, containerWidth, indicatorTranslate]);
|
|
return (
|
|
<SafeAreaView style={{ flex: 1 }} edges={["top"]}>
|
|
<ThemedView style={styles.scrollContent}>
|
|
<View style={styles.container}>
|
|
<ThemedView style={styles.header}>
|
|
<View
|
|
style={styles.segmentContainer}
|
|
onLayout={(e) => setContainerWidth(e.nativeEvent.layout.width)}
|
|
>
|
|
{/* sliding indicator */}
|
|
{containerWidth > 0 && (
|
|
<Animated.View
|
|
style={[
|
|
styles.indicator,
|
|
{
|
|
width: Math.max(containerWidth / SEGMENT_COUNT - 8, 0),
|
|
transform: [{ translateX: indicatorTranslate }],
|
|
},
|
|
]}
|
|
/>
|
|
)}
|
|
|
|
<SegmentButton
|
|
label="Tàu"
|
|
active={selected === "ships"}
|
|
onPress={() => setSelected("ships")}
|
|
/>
|
|
<SegmentButton
|
|
label="Thiết bị"
|
|
active={selected === "devices"}
|
|
onPress={() => setSelected("devices")}
|
|
/>
|
|
<SegmentButton
|
|
label="Đội tàu"
|
|
active={selected === "fleets"}
|
|
onPress={() => setSelected("fleets")}
|
|
/>
|
|
</View>
|
|
</ThemedView>
|
|
|
|
<View style={styles.contentWrapper}>
|
|
{selected === "ships" && <ShipsScreen />}
|
|
{selected === "devices" && <DevicesScreen />}
|
|
{selected === "fleets" && <FleetsScreen />}
|
|
</View>
|
|
</View>
|
|
</ThemedView>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const createStyles = (colors: typeof Colors.light, scheme: ColorScheme) =>
|
|
StyleSheet.create({
|
|
scrollContent: {
|
|
flexGrow: 1,
|
|
},
|
|
container: {
|
|
alignItems: "center",
|
|
flex: 1,
|
|
},
|
|
|
|
header: {
|
|
width: "100%",
|
|
paddingVertical: 8,
|
|
paddingHorizontal: 4,
|
|
},
|
|
segmentContainer: {
|
|
flexDirection: "row",
|
|
backgroundColor: colors.backgroundSecondary,
|
|
borderRadius: 10,
|
|
padding: 4,
|
|
alignSelf: "stretch",
|
|
},
|
|
segmentButton: {
|
|
flex: 1,
|
|
paddingVertical: 8,
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
borderRadius: 8,
|
|
},
|
|
segmentButtonActive: {
|
|
backgroundColor: scheme === "dark" ? "#435B66" : colors.surface,
|
|
shadowColor: scheme === "dark" ? "transparent" : "#000",
|
|
shadowOpacity: scheme === "dark" ? 0 : 0.1,
|
|
shadowRadius: 4,
|
|
elevation: scheme === "dark" ? 0 : 2,
|
|
},
|
|
segmentText: {
|
|
fontSize: 14,
|
|
fontWeight: "600",
|
|
color: colors.textSecondary,
|
|
},
|
|
segmentTextActive: {
|
|
color: colors.text,
|
|
},
|
|
indicator: {
|
|
position: "absolute",
|
|
left: 4,
|
|
top: 4,
|
|
bottom: 4,
|
|
backgroundColor: scheme === "dark" ? "#435B66" : colors.surface,
|
|
borderRadius: 8,
|
|
shadowColor: scheme === "dark" ? "transparent" : "#000",
|
|
shadowOpacity: scheme === "dark" ? 0 : 0.1,
|
|
shadowRadius: 4,
|
|
elevation: scheme === "dark" ? 0 : 2,
|
|
},
|
|
contentWrapper: {
|
|
flex: 1,
|
|
alignSelf: "stretch",
|
|
width: "100%",
|
|
},
|
|
});
|