feat(tv): add shared filter components and collections page support

This commit is contained in:
Fredrik Burmester
2026-01-24 10:31:03 +01:00
parent 5ce5cc2d99
commit 29873e08d7
6 changed files with 764 additions and 598 deletions

View File

@@ -0,0 +1,78 @@
import React from "react";
import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text";
import { TVTypography } from "@/constants/TVTypography";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
export interface TVFilterButtonProps {
label: string;
value: string;
onPress: () => void;
hasTVPreferredFocus?: boolean;
disabled?: boolean;
hasActiveFilter?: boolean;
}
export const TVFilterButton: React.FC<TVFilterButtonProps> = ({
label,
value,
onPress,
hasTVPreferredFocus = false,
disabled = false,
hasActiveFilter = false,
}) => {
const { focused, handleFocus, handleBlur, animatedStyle } =
useTVFocusAnimation({ scaleAmount: 1.04, duration: 120 });
return (
<Pressable
onPress={onPress}
onFocus={handleFocus}
onBlur={handleBlur}
hasTVPreferredFocus={hasTVPreferredFocus && !disabled}
disabled={disabled}
focusable={!disabled}
>
<Animated.View style={animatedStyle}>
<View
style={{
backgroundColor: focused
? "#fff"
: hasActiveFilter
? "rgba(255, 255, 255, 0.25)"
: "rgba(255,255,255,0.1)",
borderRadius: 10,
paddingVertical: 10,
paddingHorizontal: 16,
flexDirection: "row",
alignItems: "center",
gap: 8,
borderWidth: hasActiveFilter && !focused ? 1 : 0,
borderColor: "rgba(255, 255, 255, 0.4)",
}}
>
{label ? (
<Text
style={{
fontSize: TVTypography.callout,
color: focused ? "#444" : "#bbb",
}}
>
{label}
</Text>
) : null}
<Text
style={{
fontSize: TVTypography.callout,
color: focused ? "#000" : "#FFFFFF",
fontWeight: "500",
}}
numberOfLines={1}
>
{value}
</Text>
</View>
</Animated.View>
</Pressable>
);
};

View File

@@ -0,0 +1,29 @@
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import React from "react";
import { View } from "react-native";
import { Text } from "@/components/common/Text";
import { TVTypography } from "@/constants/TVTypography";
export interface TVItemCardTextProps {
item: BaseItemDto;
}
export const TVItemCardText: React.FC<TVItemCardTextProps> = ({ item }) => (
<View style={{ marginTop: 12 }}>
<Text
numberOfLines={1}
style={{ fontSize: TVTypography.callout, color: "#FFFFFF" }}
>
{item.Name}
</Text>
<Text
style={{
fontSize: TVTypography.callout - 2,
color: "#9CA3AF",
marginTop: 2,
}}
>
{item.ProductionYear}
</Text>
</View>
);

View File

@@ -25,8 +25,12 @@ export type { TVControlButtonProps } from "./TVControlButton";
export { TVControlButton } from "./TVControlButton";
export type { TVFavoriteButtonProps } from "./TVFavoriteButton";
export { TVFavoriteButton } from "./TVFavoriteButton";
export type { TVFilterButtonProps } from "./TVFilterButton";
export { TVFilterButton } from "./TVFilterButton";
export type { TVFocusablePosterProps } from "./TVFocusablePoster";
export { TVFocusablePoster } from "./TVFocusablePoster";
export type { TVItemCardTextProps } from "./TVItemCardText";
export { TVItemCardText } from "./TVItemCardText";
export type { TVLanguageCardProps } from "./TVLanguageCard";
export { TVLanguageCard } from "./TVLanguageCard";
export type { TVMetadataBadgesProps } from "./TVMetadataBadges";