110 lines
3.4 KiB
TypeScript
110 lines
3.4 KiB
TypeScript
import React, { useEffect, useRef } from "react";
|
|
import { Animated, StyleSheet, View } from "react-native";
|
|
import { LinearGradient } from "expo-linear-gradient";
|
|
import type { RootTabParamList } from "../../types/navigation";
|
|
import lineColorList from "../../assets/originData/lineColorList";
|
|
import { stationIDPair } from "../../lib/getStationList";
|
|
import { fixedColors } from "../../lib/theme/colors";
|
|
|
|
export type TabBarBackgroundProps = {
|
|
routeName: keyof RootTabParamList;
|
|
selectedLine?: string | null;
|
|
isDark: boolean;
|
|
isExtraWindowOpen: boolean;
|
|
};
|
|
|
|
export type TabBarAppearance = {
|
|
lineColor: string | null;
|
|
lineColorDark: string | null;
|
|
showGradient: boolean;
|
|
defaultBg: string;
|
|
activeTintColor: string;
|
|
inactiveTintColor: string;
|
|
};
|
|
|
|
const getLineColor = (selectedLine?: string | null) => {
|
|
if (!selectedLine) return null;
|
|
const lineCode = stationIDPair[selectedLine as keyof typeof stationIDPair];
|
|
return lineCode ? lineColorList[lineCode] : null;
|
|
};
|
|
|
|
const darkenHex = (hex: string, factor: number) => {
|
|
const h = hex.replace("#", "");
|
|
const r = Math.round(parseInt(h.slice(0, 2), 16) * factor);
|
|
const g = Math.round(parseInt(h.slice(2, 4), 16) * factor);
|
|
const b = Math.round(parseInt(h.slice(4, 6), 16) * factor);
|
|
return `#${[r, g, b].map((v) => Math.min(255, v).toString(16).padStart(2, "0")).join("")}`;
|
|
};
|
|
|
|
export const getTabBarAppearance = ({
|
|
routeName,
|
|
selectedLine,
|
|
isDark,
|
|
isExtraWindowOpen,
|
|
}: TabBarBackgroundProps): TabBarAppearance => {
|
|
const lineColor = getLineColor(selectedLine);
|
|
const lineColorDark = lineColor ? darkenHex(lineColor, 0.78) : null;
|
|
const showGradient =
|
|
routeName === "positions" && !!lineColor && !!lineColorDark;
|
|
const defaultBg = isDark ? "#1c1c1e" : "white";
|
|
const defaultActive = isDark ? "#ffffff" : "#007AFF";
|
|
const defaultInactive = isDark ? "#8e8e93" : "#8e8e93";
|
|
const useLightTint = showGradient || isExtraWindowOpen;
|
|
|
|
return {
|
|
lineColor,
|
|
lineColorDark,
|
|
showGradient,
|
|
defaultBg,
|
|
activeTintColor: useLightTint ? "white" : defaultActive,
|
|
inactiveTintColor: useLightTint
|
|
? "rgba(255,255,255,0.75)"
|
|
: defaultInactive,
|
|
};
|
|
};
|
|
|
|
export function TabBarBackground(props: TabBarBackgroundProps) {
|
|
const { isExtraWindowOpen } = props;
|
|
const fadeAnim = useRef(new Animated.Value(0)).current;
|
|
const { lineColor, lineColorDark, showGradient, defaultBg } =
|
|
getTabBarAppearance(props);
|
|
|
|
useEffect(() => {
|
|
Animated.timing(fadeAnim, {
|
|
toValue: isExtraWindowOpen ? 1 : 0,
|
|
duration: 300,
|
|
useNativeDriver: false,
|
|
}).start();
|
|
}, [isExtraWindowOpen]);
|
|
|
|
return (
|
|
<View style={{ flex: 1 }}>
|
|
{/* 路線カラー or デフォルト背景 */}
|
|
{showGradient ? (
|
|
<LinearGradient
|
|
colors={[lineColor!, lineColorDark!]}
|
|
start={{ x: 0, y: 0 }}
|
|
end={{ x: 0, y: 1 }}
|
|
style={StyleSheet.absoluteFill}
|
|
/>
|
|
) : (
|
|
<View
|
|
style={{
|
|
...StyleSheet.absoluteFill,
|
|
backgroundColor: defaultBg,
|
|
}}
|
|
/>
|
|
)}
|
|
{/* 追加ウィンドウ時の青グラデーション(フェードイン/アウト) */}
|
|
<Animated.View style={{ ...StyleSheet.absoluteFill, opacity: fadeAnim }}>
|
|
<LinearGradient
|
|
colors={[fixedColors.primary, fixedColors.primaryDark]}
|
|
start={{ x: 0, y: 0 }}
|
|
end={{ x: 0, y: 1 }}
|
|
style={{ flex: 1 }}
|
|
/>
|
|
</Animated.View>
|
|
</View>
|
|
);
|
|
}
|