106 lines
3.1 KiB
TypeScript
106 lines
3.1 KiB
TypeScript
import { useState, useEffect, useMemo, useCallback } from "react";
|
|
|
|
interface UseTreeViewOptions<T> {
|
|
data: T[];
|
|
searchFields: (keyof T)[];
|
|
groupBy: (item: T) => string;
|
|
searchTerm: string;
|
|
onSearchChange: (term: string) => void;
|
|
availableCategories?: string[]; // Nouvelles catégories disponibles
|
|
}
|
|
|
|
export function useTreeView<T>({
|
|
data,
|
|
searchFields,
|
|
groupBy,
|
|
searchTerm,
|
|
onSearchChange,
|
|
availableCategories = [],
|
|
}: UseTreeViewOptions<T>) {
|
|
// État pour les catégories ouvertes/fermées
|
|
const [expandedCategories, setExpandedCategories] = useState<Set<string>>(
|
|
new Set()
|
|
);
|
|
|
|
// Grouper les données par catégorie et filtrer en fonction de la recherche
|
|
const filteredDataByCategory = useMemo(() => {
|
|
// Grouper les données par catégorie
|
|
const dataByCategory = data.reduce((acc, item) => {
|
|
const categoryKey = groupBy(item);
|
|
if (!acc[categoryKey]) {
|
|
acc[categoryKey] = [];
|
|
}
|
|
acc[categoryKey].push(item);
|
|
return acc;
|
|
}, {} as Record<string, T[]>);
|
|
|
|
// Filtrer les données en fonction de la recherche
|
|
const filteredCategories = Object.entries(dataByCategory).reduce(
|
|
(acc, [category, categoryItems]) => {
|
|
const filteredItems = categoryItems.filter((item) => {
|
|
const matchesSearch = searchFields.some((field) => {
|
|
const value = item[field];
|
|
if (typeof value === "string") {
|
|
return value.toLowerCase().includes(searchTerm.toLowerCase());
|
|
}
|
|
return false;
|
|
});
|
|
return matchesSearch;
|
|
});
|
|
|
|
if (filteredItems.length > 0) {
|
|
acc[category] = filteredItems;
|
|
}
|
|
return acc;
|
|
},
|
|
{} as Record<string, T[]>
|
|
);
|
|
|
|
// Ajouter les catégories vides qui sont dans availableCategories
|
|
availableCategories.forEach(category => {
|
|
if (!filteredCategories[category]) {
|
|
filteredCategories[category] = [];
|
|
}
|
|
});
|
|
|
|
return filteredCategories;
|
|
}, [data, searchFields, groupBy, searchTerm, availableCategories]);
|
|
|
|
// Fonctions pour gérer l'expansion des catégories
|
|
const toggleCategory = useCallback((category: string) => {
|
|
setExpandedCategories((prev) => {
|
|
const newExpanded = new Set(prev);
|
|
if (newExpanded.has(category)) {
|
|
newExpanded.delete(category);
|
|
} else {
|
|
newExpanded.add(category);
|
|
}
|
|
return newExpanded;
|
|
});
|
|
}, []);
|
|
|
|
const expandAll = useCallback(() => {
|
|
setExpandedCategories(new Set(Object.keys(filteredDataByCategory)));
|
|
}, [filteredDataByCategory]);
|
|
|
|
const collapseAll = useCallback(() => {
|
|
setExpandedCategories(new Set());
|
|
}, []);
|
|
|
|
// Ouvrir automatiquement les catégories qui contiennent des résultats lors de la recherche
|
|
useEffect(() => {
|
|
if (searchTerm.trim()) {
|
|
const categoriesWithResults = Object.keys(filteredDataByCategory);
|
|
setExpandedCategories(new Set(categoriesWithResults));
|
|
}
|
|
}, [searchTerm, filteredDataByCategory]);
|
|
|
|
return {
|
|
filteredDataByCategory,
|
|
expandedCategories,
|
|
toggleCategory,
|
|
expandAll,
|
|
collapseAll,
|
|
};
|
|
}
|