POWR/components/library/FilterSheet.tsx
2025-02-12 12:55:51 -05:00

128 lines
3.8 KiB
TypeScript

import React from 'react';
import { View } from 'react-native';
import { Text } from '@/components/ui/text';
import { Button } from '@/components/ui/button';
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet';
import { Badge } from '@/components/ui/badge';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
export type SourceType = 'local' | 'powr' | 'nostr';
export interface FilterOptions {
equipment: string[];
tags: string[];
source: SourceType[];
}
interface FilterSheetProps {
isOpen: boolean;
onClose: () => void;
options: FilterOptions;
onApplyFilters: (filters: FilterOptions) => void;
availableFilters: {
equipment: string[];
tags: string[];
source: SourceType[];
};
}
function renderFilterSection<T extends string>(
title: string,
category: keyof FilterOptions,
values: T[],
selectedValues: T[],
onToggle: (category: keyof FilterOptions, value: T) => void
) {
return (
<AccordionItem value={category}>
<AccordionTrigger>
<View className="flex-row items-center gap-2">
<Text className="text-base font-medium">{title}</Text>
{selectedValues.length > 0 && (
<Badge variant="secondary">
<Text>{selectedValues.length}</Text>
</Badge>
)}
</View>
</AccordionTrigger>
<AccordionContent>
<View className="flex-row flex-wrap gap-2">
{values.map(value => {
const isSelected = selectedValues.includes(value);
return (
<Button
key={value}
variant={isSelected ? 'purple' : 'outline'}
onPress={() => onToggle(category, value)}
>
<Text className={isSelected ? 'text-white' : ''}>{value}</Text>
</Button>
);
})}
</View>
</AccordionContent>
</AccordionItem>
);
}
export function FilterSheet({
isOpen,
onClose,
options,
onApplyFilters,
availableFilters
}: FilterSheetProps) {
const [localOptions, setLocalOptions] = React.useState(options);
const handleReset = () => {
const resetOptions = { equipment: [], tags: [], source: [] };
setLocalOptions(resetOptions);
// Immediately apply reset
onApplyFilters(resetOptions);
};
const toggleFilter = (
category: keyof FilterOptions,
value: string | SourceType
) => {
setLocalOptions(prev => ({
...prev,
[category]: prev[category].includes(value as any)
? prev[category].filter(v => v !== value)
: [...prev[category], value as any]
}));
};
return (
<Sheet isOpen={isOpen} onClose={onClose}>
<SheetContent>
<View className="gap-4">
<Accordion type="single" collapsible>
{renderFilterSection('Equipment', 'equipment', availableFilters.equipment, localOptions.equipment, toggleFilter)}
{renderFilterSection('Tags', 'tags', availableFilters.tags, localOptions.tags, toggleFilter)}
{renderFilterSection<SourceType>('Source', 'source', availableFilters.source, localOptions.source, toggleFilter)}
</Accordion>
<View className="flex-row gap-2 mt-4">
<Button
variant="outline"
className="flex-1"
onPress={handleReset}
>
<Text>Reset</Text>
</Button>
<Button
className="flex-1"
variant="purple"
onPress={() => {
onApplyFilters(localOptions);
onClose();
}}
>
<Text className="text-white font-semibold">Apply Filters</Text>
</Button>
</View>
</View>
</SheetContent>
</Sheet>
);
}