111 lines
3.4 KiB
TypeScript
111 lines
3.4 KiB
TypeScript
import React, { useState } from "react";
|
|
import {
|
|
View,
|
|
Text,
|
|
TouchableOpacity,
|
|
Animated,
|
|
LayoutAnimation,
|
|
Platform,
|
|
UIManager,
|
|
useColorScheme,
|
|
} from "react-native";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import ExpandableItem, { FeedItem } from "./ExpandableItem";
|
|
|
|
if (Platform.OS === "android") {
|
|
UIManager.setLayoutAnimationEnabledExperimental?.(true);
|
|
}
|
|
|
|
export interface Category {
|
|
id: string;
|
|
label: string;
|
|
icon: keyof typeof Ionicons.glyphMap;
|
|
color: string; // accent hex for the icon badge
|
|
darkColor: string;
|
|
items: FeedItem[];
|
|
}
|
|
|
|
interface CollapsibleCategoryProps {
|
|
category: Category;
|
|
defaultOpen?: boolean;
|
|
}
|
|
|
|
export default function CollapsibleCategory({
|
|
category,
|
|
defaultOpen = false,
|
|
}: CollapsibleCategoryProps) {
|
|
const [open, setOpen] = useState(defaultOpen);
|
|
const scheme = useColorScheme();
|
|
const dark = scheme === "dark";
|
|
|
|
const accentColor = dark ? category.darkColor : category.color;
|
|
|
|
const toggle = () => {
|
|
LayoutAnimation.configureNext({
|
|
duration: 260,
|
|
create: { type: "easeInEaseOut", property: "opacity" },
|
|
update: { type: "spring", springDamping: 0.85 },
|
|
});
|
|
setOpen((v) => !v);
|
|
};
|
|
|
|
return (
|
|
<View className="mb-4">
|
|
<TouchableOpacity
|
|
onPress={toggle}
|
|
activeOpacity={0.8}
|
|
className={`
|
|
mx-4 flex-row items-center px-4 py-3.5 rounded-2xl
|
|
${dark
|
|
? "bg-obsidian-50 border border-border-dark"
|
|
: "bg-parchment-100 border border-border-light"
|
|
}
|
|
`}
|
|
>
|
|
{/* Icon badge */}
|
|
<View
|
|
className="w-8 h-8 rounded-xl items-center justify-center mr-3"
|
|
style={{ backgroundColor: accentColor + "25" }}
|
|
>
|
|
<Ionicons name={category.icon} size={16} color={accentColor} />
|
|
</View>
|
|
|
|
<Text
|
|
className={`flex-1 text-sm font-sans font-bold tracking-wide ${
|
|
dark ? "text-ink-dark" : "text-ink-light"
|
|
}`}
|
|
>
|
|
{category.label}
|
|
</Text>
|
|
|
|
{/* Count badge */}
|
|
<View
|
|
className="px-2 py-0.5 rounded-full mr-3"
|
|
style={{ backgroundColor: accentColor + "20" }}
|
|
>
|
|
<Text
|
|
className="text-xs font-sans font-semibold"
|
|
style={{ color: accentColor }}
|
|
>
|
|
{category.items.length}
|
|
</Text>
|
|
</View>
|
|
|
|
<Ionicons
|
|
name={open ? "chevron-up" : "chevron-down"}
|
|
size={16}
|
|
color={dark ? "#706D67" : "#8A8278"}
|
|
/>
|
|
</TouchableOpacity>
|
|
|
|
{/* Items */}
|
|
{open && (
|
|
<View className="mt-2">
|
|
{category.items.map((item) => (
|
|
<ExpandableItem key={item.id} item={item} />
|
|
))}
|
|
</View>
|
|
)}
|
|
</View>
|
|
);
|
|
} |