import { View, Text, TouchableOpacity, StyleSheet, ScrollView, } from "react-native"; import { useEffect, useState } from "react"; import type { LucideIcon } from "../../icons"; import { Filter, Plus, Copy, Eye, Info } from "../../icons"; import { macOSColors } from "../gameUI/constants/macOSDesignSystemColors"; import { SectionHeader } from "./SectionHeader"; import { FilterList, AddFilterInput, AddFilterButton, } from "./FilterComponents"; import { useFilterManager } from "../../hooks/useFilterManager"; export interface FilterSection { id: string; title: string; icon?: LucideIcon; color?: string; type: "status" | "method" | "contentType" | "custom" | "patterns"; data?: FilterOption[]; renderCustom?: () => React.ReactNode; description?: string; } export interface FilterOption { id: string; label: string; count?: number; icon?: LucideIcon; color?: string; backgroundColor?: string; borderColor?: string; isActive?: boolean; value?: any; description?: string; } export interface DynamicFilterConfig { sections?: FilterSection[]; /** Filter summary section - shows active filter state and result counts */ filterSummarySection?: { enabled: boolean; totalCount: number; filteredCount: number; includePatterns?: Set; excludePatterns?: Set; }; addFilterSection?: { enabled: boolean; placeholder?: string; title?: string; icon?: LucideIcon; }; /** Include-only patterns section - shows queries matching ANY of these patterns */ includeOnlySection?: { enabled: boolean; placeholder?: string; title?: string; description?: string; icon?: LucideIcon; patterns: Set; onPatternToggle: (pattern: string) => void; onPatternAdd: (pattern: string) => void; }; availableItemsSection?: { enabled: boolean; title?: string; emptyMessage?: string; icon?: LucideIcon; items?: string[]; }; howItWorksSection?: { enabled: boolean; title?: string; description?: string; examples?: string[]; icon?: LucideIcon; }; previewSection?: { enabled: boolean; title?: string; content: () => React.ReactNode; icon?: LucideIcon; headerActions?: () => React.ReactNode; }; onFilterChange?: (filterId: string, value: any) => void; onPatternToggle?: (pattern: string) => void; onPatternAdd?: (pattern: string) => void; activePatterns?: Set; tabs?: { id: string; label: string; icon?: LucideIcon; count?: number; content: () => React.ReactNode; }[]; activeTab?: string; onTabChange?: (tabId: string) => void; } interface DynamicFilterViewProps extends DynamicFilterConfig { className?: string; } export function DynamicFilterView({ sections = [], filterSummarySection, addFilterSection, includeOnlySection, availableItemsSection, howItWorksSection, previewSection, onFilterChange, onPatternToggle, onPatternAdd, activePatterns = new Set(), tabs, activeTab, onTabChange, }: DynamicFilterViewProps) { const filterManager = useFilterManager(activePatterns); const includeFilterManager = useFilterManager(includeOnlySection?.patterns || new Set()); const [internalActiveTab, setInternalActiveTab] = useState( tabs?.[0]?.id || "" ); const currentActiveTab = activeTab || internalActiveTab; useEffect(() => { if ( activePatterns.size !== filterManager.filters.size || !Array.from(activePatterns).every((p) => filterManager.filters.has(p)) ) { // Sync external changes } }, [activePatterns, filterManager.filters]); const handleAddPattern = () => { if (filterManager.newFilter.trim() && onPatternAdd) { onPatternAdd(filterManager.newFilter.trim()); filterManager.addFilter(filterManager.newFilter); } }; const handleAddIncludePattern = () => { if (includeFilterManager.newFilter.trim() && includeOnlySection?.onPatternAdd) { includeOnlySection.onPatternAdd(includeFilterManager.newFilter.trim()); includeFilterManager.addFilter(includeFilterManager.newFilter); } }; const suggestedItems = availableItemsSection?.items?.filter((item) => { return !Array.from(activePatterns).some((pattern) => item.includes(pattern) ); }) || []; const renderTabs = () => { if (!tabs || tabs.length === 0) return null; return ( {tabs.map((tab) => ( { if (onTabChange) onTabChange(tab.id); else setInternalActiveTab(tab.id); }} style={[ styles.tabButton, currentActiveTab === tab.id ? styles.tabButtonActive : styles.tabButtonInactive, ]} > {tab.icon && ( )} {tab.label} {tab.count !== undefined && tab.count > 0 && ( {tab.count} )} ))} ); }; const renderFilterSection = (section: FilterSection) => { if (section.type === "custom" && section.renderCustom) { return section.renderCustom(); } if (section.type === "patterns") { return null; // Handled separately } if (!section.data || section.data.length === 0) return null; return ( {section.icon && ( )} {section.title} {section.description && ( {section.description} )} {section.data.map((option) => { const hasCount = option.count !== undefined; const countIsZero = hasCount && option.count === 0; const showLabel = section.type !== "method" || option.icon; return ( onFilterChange?.(option.id, option.value)} > {/* Icon or Method Badge */} {option.icon ? ( ) : section.type === "method" ? ( {option.label} ) : null} {/* Count - Prominent when >0, subtle when =0 */} {hasCount && ( {option.count} )} {/* Label - Only show when it adds context */} {showLabel && ( {option.label} )} {/* Description for special cases like Quick Presets */} {option.description && ( {option.description} )} ); })} ); }; const renderContent = () => { if (tabs && currentActiveTab) { const activeTabData = tabs.find((tab) => tab.id === currentActiveTab); if (activeTabData?.content) { return activeTabData.content(); } } return ( <> {/* Filter Summary Section - Shows active filter state */} {filterSummarySection?.enabled && ( filterSummarySection.includePatterns?.size || filterSummarySection.excludePatterns?.size ) ? ( Filters Active Showing {filterSummarySection.filteredCount} of {filterSummarySection.totalCount} queries {filterSummarySection.includePatterns?.size ? ( {" "}• Including: {Array.from(filterSummarySection.includePatterns).join(", ")} ) : null} {filterSummarySection.excludePatterns?.size ? ( {" "}• Excluding: {Array.from(filterSummarySection.excludePatterns).join(", ")} ) : null} ) : null} {/* Status sections first */} {sections.map(renderFilterSection)} {/* Include Only Section - Show ONLY queries matching these patterns */} {includeOnlySection?.enabled && ( {includeOnlySection.title || "INCLUDE ONLY FILTERS"} {includeOnlySection.patterns.size > 0 && ( )} {includeOnlySection.description && ( {includeOnlySection.description} )} {!includeFilterManager.showAddInput ? ( includeFilterManager.setShowAddInput(true)} color={macOSColors.semantic.success} label="Add include pattern" /> ) : ( { includeFilterManager.setShowAddInput(false); includeFilterManager.setNewFilter(""); }} placeholder={includeOnlySection.placeholder || "Enter pattern to include..."} color={macOSColors.semantic.success} /> )} {includeOnlySection.patterns.size > 0 && ( )} )} {/* Exclude Filter Section - Hide queries matching these patterns */} {addFilterSection?.enabled && ( {addFilterSection.title || "EXCLUDE FILTERS"} {activePatterns.size > 0 && ( )} Hide queries matching these patterns from the list. {!filterManager.showAddInput ? ( filterManager.setShowAddInput(true)} color={macOSColors.semantic.info} label="Add exclude pattern" /> ) : ( { filterManager.setShowAddInput(false); filterManager.setNewFilter(""); }} placeholder={addFilterSection.placeholder || "Enter pattern to exclude..."} color={macOSColors.semantic.info} /> )} {activePatterns.size > 0 && ( )} )} {availableItemsSection?.enabled && ( {availableItemsSection.title || "AVAILABLE ITEMS"} {suggestedItems.length > 0 ? ( suggestedItems.map((item) => ( { if (onPatternAdd) { onPatternAdd(item); filterManager.addFilter(item); } }} style={styles.availableKeyItem} sentry-label="ignore-touchable-opacity" > {item} )) ) : ( {availableItemsSection.emptyMessage || "No items available"} )} )} {howItWorksSection?.enabled && ( {howItWorksSection.title || "HOW FILTERS WORK"} {howItWorksSection.description || "Filters help you focus on relevant data by hiding unwanted items."} {howItWorksSection.examples && howItWorksSection.examples.length > 0 && ( EXAMPLES: {howItWorksSection.examples.map((example, index) => ( {example} ))} )} )} {previewSection?.enabled && ( {previewSection.title || "PREVIEW"} {previewSection.headerActions && ( {previewSection.headerActions()} )} {previewSection.content()} )} ); }; return ( {renderTabs()} {renderContent()} ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: macOSColors.background.base, }, content: { flex: 1, }, scrollContent: { paddingTop: 16, paddingHorizontal: 16, paddingBottom: 24, }, tabContainer: { flexDirection: "row", paddingHorizontal: 16, paddingVertical: 12, gap: 8, backgroundColor: macOSColors.background.card, borderBottomWidth: 1, borderBottomColor: macOSColors.border.default, }, tabButton: { flex: 1, flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 6, paddingVertical: 8, paddingHorizontal: 12, borderRadius: 6, borderWidth: 1, }, tabButtonActive: { backgroundColor: macOSColors.semantic.infoBackground, borderColor: macOSColors.semantic.info, }, tabButtonInactive: { backgroundColor: macOSColors.background.hover, borderColor: macOSColors.border.default, }, tabButtonText: { fontSize: 11, fontWeight: "600", letterSpacing: 0.5, }, tabButtonTextActive: { color: macOSColors.semantic.info, }, tabButtonTextInactive: { color: macOSColors.text.muted, }, tabBadge: { backgroundColor: macOSColors.semantic.info + "40", paddingHorizontal: 6, paddingVertical: 1, borderRadius: 8, minWidth: 18, alignItems: "center", }, tabBadgeText: { fontSize: 9, color: macOSColors.semantic.info, fontWeight: "700", }, section: { marginBottom: 8, }, filterInputWrapper: { marginBottom: 4, }, filterGrid: { flexDirection: "row", flexWrap: "wrap", gap: 8, marginTop: 8, }, filterCard: { backgroundColor: "transparent", borderRadius: 8, paddingHorizontal: 4, paddingVertical: 6, flexDirection: "column", alignItems: "center", justifyContent: "center", borderWidth: 1, borderColor: macOSColors.border.default, width: 68, minHeight: 62, }, activeFilterCard: { backgroundColor: macOSColors.semantic.infoBackground + "14", borderColor: macOSColors.semantic.info, borderWidth: 2, }, filterIcon: { marginBottom: 0, }, filterCountActive: { fontSize: 14, fontWeight: "700", color: macOSColors.text.primary, fontFamily: "monospace", marginTop: 3, marginBottom: 1, textShadowColor: "rgba(0, 0, 0, 0.2)", textShadowOffset: { width: 0, height: 0.5 }, textShadowRadius: 1, }, filterCountZero: { fontSize: 11, fontWeight: "500", color: macOSColors.text.muted, fontFamily: "monospace", marginTop: 3, marginBottom: 1, }, filterLabelCompact: { fontSize: 10, color: macOSColors.text.primary, fontWeight: "600", textAlign: "center", textTransform: "capitalize", textShadowColor: "rgba(0, 0, 0, 0.3)", textShadowOffset: { width: 0, height: 0.5 }, textShadowRadius: 1, }, filterDescriptionBottom: { fontSize: 8, color: macOSColors.text.muted, textAlign: "center", marginTop: 2, lineHeight: 11, }, sectionDescription: { fontSize: 10, color: macOSColors.text.secondary, marginTop: 4, marginBottom: 4, lineHeight: 14, }, methodBadgeCompact: { paddingHorizontal: 8, paddingVertical: 4, borderRadius: 5, minWidth: 48, maxWidth: 60, alignItems: "center", justifyContent: "center", }, methodTextCompact: { fontSize: 13, fontWeight: "700", letterSpacing: 0.3, fontFamily: "monospace", }, activeFiltersSection: { backgroundColor: macOSColors.background.card, borderRadius: 8, borderWidth: 1, borderColor: macOSColors.border.default + "50", marginTop: 8, overflow: "hidden", }, activeFiltersContent: { paddingHorizontal: 16, paddingTop: 8, paddingBottom: 16, maxHeight: 200, }, includeOnlySection: { backgroundColor: macOSColors.background.card, borderRadius: 8, borderWidth: 1, borderColor: macOSColors.semantic.success + "40", marginTop: 12, overflow: "hidden", }, includeOnlyDescription: { fontSize: 10, color: macOSColors.text.secondary, paddingHorizontal: 16, paddingTop: 8, lineHeight: 14, }, includeOnlyInputWrapper: { paddingHorizontal: 16, paddingTop: 8, paddingBottom: 8, }, includeOnlyFiltersContent: { paddingHorizontal: 16, paddingBottom: 16, maxHeight: 150, }, excludeFiltersSection: { backgroundColor: macOSColors.background.card, borderRadius: 8, borderWidth: 1, borderColor: macOSColors.semantic.info + "40", marginTop: 12, overflow: "hidden", }, excludeFiltersDescription: { fontSize: 10, color: macOSColors.text.secondary, paddingHorizontal: 16, paddingTop: 8, lineHeight: 14, }, excludeFiltersInputWrapper: { paddingHorizontal: 16, paddingTop: 8, paddingBottom: 8, }, excludeFiltersContent: { paddingHorizontal: 16, paddingBottom: 16, maxHeight: 150, }, filterSummarySection: { backgroundColor: macOSColors.semantic.warning + "15", borderRadius: 8, borderWidth: 1, borderColor: macOSColors.semantic.warning + "40", padding: 12, marginBottom: 12, }, filterSummaryHeader: { flexDirection: "row", alignItems: "center", gap: 6, marginBottom: 6, }, filterSummaryTitle: { fontSize: 12, fontWeight: "600", color: macOSColors.semantic.warning, }, filterSummaryText: { fontSize: 11, color: macOSColors.text.primary, lineHeight: 16, }, filterSummaryInclude: { color: macOSColors.semantic.success, fontWeight: "500", }, filterSummaryExclude: { color: macOSColors.semantic.info, fontWeight: "500", }, emptyStateText: { fontSize: 11, color: macOSColors.text.muted, fontStyle: "italic", textAlign: "center", paddingVertical: 12, }, availableKeysSection: { backgroundColor: macOSColors.background.card, borderRadius: 8, borderWidth: 1, borderColor: macOSColors.border.default + "50", marginTop: 12, overflow: "hidden", }, availableKeysScroll: { maxHeight: 150, paddingHorizontal: 16, paddingTop: 8, paddingBottom: 16, }, availableKeyItem: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingVertical: 8, paddingHorizontal: 10, backgroundColor: macOSColors.background.input, borderRadius: 6, borderWidth: 1, borderColor: macOSColors.border.input, marginBottom: 6, }, availableKeyText: { flex: 1, fontSize: 11, color: macOSColors.text.primary, fontFamily: "monospace", marginRight: 8, }, howItWorksSection: { backgroundColor: macOSColors.background.card, borderRadius: 8, borderWidth: 1, borderColor: macOSColors.border.default + "50", marginTop: 12, overflow: "hidden", }, howItWorksText: { fontSize: 11, color: macOSColors.text.secondary, lineHeight: 16, marginBottom: 12, marginTop: 8, paddingHorizontal: 16, fontFamily: "monospace", }, examplesContainer: { paddingTop: 8, paddingHorizontal: 16, paddingBottom: 16, borderTopWidth: 1, borderTopColor: macOSColors.border.default + "50", }, examplesTitle: { fontSize: 10, fontWeight: "600", color: macOSColors.text.muted, fontFamily: "monospace", letterSpacing: 0.5, marginBottom: 6, }, exampleItem: { fontSize: 10, color: macOSColors.text.muted, fontFamily: "monospace", lineHeight: 16, }, previewSection: { backgroundColor: macOSColors.background.card, borderRadius: 8, borderWidth: 1, borderColor: macOSColors.border.default + "50", marginTop: 12, overflow: "hidden", }, previewContent: { paddingHorizontal: 16, paddingTop: 8, paddingBottom: 16, }, });