etfoglasi-frontend/components/ExpandableItem.tsx

153 lines
5.0 KiB
TypeScript

import React, { useState, useRef } from "react";
import {
View,
Text,
TouchableOpacity,
Animated,
LayoutAnimation,
Platform,
UIManager,
useColorScheme,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
// Enable LayoutAnimation on Android idk honestly probably ok?
if (Platform.OS === "android") {
UIManager.setLayoutAnimationEnabledExperimental?.(true);
}
export interface FeedItem {
id: string;
title: string;
author: string;
timestamp: string;
tag: string;
paragraphs: string[];
}
interface ExpandableItemProps {
item: FeedItem;
}
export default function ExpandableItem({ item }: ExpandableItemProps) {
const [expanded, setExpanded] = useState(false);
const rotateAnim = useRef(new Animated.Value(0)).current;
const scheme = useColorScheme();
const dark = scheme === "dark";
const toggle = () => {
LayoutAnimation.configureNext({
duration: 280,
create: { type: "easeInEaseOut", property: "opacity" },
update: { type: "spring", springDamping: 0.8 },
});
Animated.timing(rotateAnim, {
toValue: expanded ? 0 : 1,
duration: 250,
useNativeDriver: true,
}).start();
setExpanded((v) => !v);
};
const chevronRotation = rotateAnim.interpolate({
inputRange: [0, 1],
outputRange: ["0deg", "180deg"],
});
return (
<TouchableOpacity
activeOpacity={0.85}
onPress={toggle}
className={`
mx-4 mb-3 rounded-2xl overflow-hidden
${dark
? "bg-obsidian-100 border border-border-dark"
: "bg-surface-light border border-border-light"
}
`}
style={{
shadowColor: dark ? "#000" : "#1A1714",
shadowOpacity: dark ? 0.35 : 0.06,
shadowOffset: { width: 0, height: 2 },
shadowRadius: 8,
elevation: 2,
}}
>
{/* Header */}
<View className="flex-row items-center px-4 py-4">
<View className="flex-1 pr-3">
{/* Tag + timestamp row */}
<View className="flex-row items-center mb-1 gap-2">
<View
className={`px-2 py-0.5 rounded-full ${
dark ? "bg-accent-dark/20" : "bg-accent/10"
}`}
>
<Text
className={`text-xs font-sans font-semibold tracking-wide ${
dark ? "text-accent-dark" : "text-accent"
}`}
>
{item.tag}
</Text>
</View>
<Text
className={`text-xs ${
dark ? "text-muted-dark" : "text-muted-light"
}`}
>
{item.timestamp}
</Text>
</View>
{/* Title */}
<Text
className={`text-base font-sans font-semibold leading-snug ${
dark ? "text-ink-dark" : "text-ink-light"
}`}
numberOfLines={expanded ? undefined : 2}
>
{item.title}
</Text>
{/* Author */}
<Text
className={`text-xs mt-1 ${
dark ? "text-muted-dark" : "text-muted-light"
}`}
>
by {item.author}
</Text>
</View>
<Animated.View style={{ transform: [{ rotate: chevronRotation }] }}>
<Ionicons
name="chevron-down"
size={18}
color={dark ? "#706D67" : "#8A8278"}
/>
</Animated.View>
</View>
{/* Expanded body */}
{expanded && (
<View
className={`px-4 pb-5 pt-1 border-t ${
dark ? "border-border-dark" : "border-border-light"
}`}
>
{item.paragraphs.map((para, i) => (
<Text
key={i}
className={`text-sm font-sans leading-relaxed mb-3 last:mb-0 ${
dark ? "text-ink-dark/80" : "text-ink-light/80"
}`}
>
{para}
</Text>
))}
</View>
)}
</TouchableOpacity>
);
}