import { useEffect, useState } from "react"; import Fuse from "fuse.js"; import { groupBy } from "./shared"; /** * Helpers for the SelectMapModal and SelectTokenModal */ // Helper for generating search results for items export function useSearch(items, search) { const [filteredItems, setFilteredItems] = useState([]); const [filteredItemScores, setFilteredItemScores] = useState({}); const [fuse, setFuse] = useState(); // Update search index when items change useEffect(() => { setFuse(new Fuse(items, { keys: ["name", "group"], includeScore: true })); }, [items]); // Perform search when search changes useEffect(() => { if (search) { const query = fuse.search(search); setFilteredItems(query.map((result) => result.item)); setFilteredItemScores( query.reduce( (acc, value) => ({ ...acc, [value.item.id]: value.score }), {} ) ); } }, [search, items, fuse]); return [filteredItems, filteredItemScores]; } // Helper for grouping items export function useGroup(items, filteredItems, useFiltered, filteredScores) { const itemsByGroup = groupBy(useFiltered ? filteredItems : items, "group"); // Get the groups of the items sorting by the average score if we're filtering or the alphabetical order // with "" at the start and "default" at the end if not let itemGroups = Object.keys(itemsByGroup); if (useFiltered) { itemGroups.sort((a, b) => { const aScore = itemsByGroup[a].reduce( (acc, item) => (acc + filteredScores[item.id]) / 2 ); const bScore = itemsByGroup[b].reduce( (acc, item) => (acc + filteredScores[item.id]) / 2 ); return aScore - bScore; }); } else { itemGroups.sort((a, b) => { if (a === "" || b === "default") { return -1; } if (b === "" || a === "default") { return 1; } return a.localeCompare(b); }); } return [itemsByGroup, itemGroups]; } // Helper for handling selecting items export function handleItemSelect( item, selectMode, selectedIds, setSelectedIds, itemsByGroup, itemGroups ) { if (!item) { setSelectedIds([]); return; } switch (selectMode) { case "single": setSelectedIds([item.id]); break; case "multiple": setSelectedIds((prev) => { if (prev.includes(item.id)) { return prev.filter((id) => id !== item.id); } else { return [...prev, item.id]; } }); break; case "range": // Create items array let items = itemGroups.reduce( (acc, group) => [...acc, ...itemsByGroup[group]], [] ); // Add all items inbetween the previous selected item and the current selected if (selectedIds.length > 0) { const mapIndex = items.findIndex((m) => m.id === item.id); const lastIndex = items.findIndex( (m) => m.id === selectedIds[selectedIds.length - 1] ); let idsToAdd = []; let idsToRemove = []; const direction = mapIndex > lastIndex ? 1 : -1; for ( let i = lastIndex + direction; direction < 0 ? i >= mapIndex : i <= mapIndex; i += direction ) { const itemId = items[i].id; if (selectedIds.includes(itemId)) { idsToRemove.push(itemId); } else { idsToAdd.push(itemId); } } setSelectedIds((prev) => { let ids = [...prev, ...idsToAdd]; return ids.filter((id) => !idsToRemove.includes(id)); }); } else { setSelectedIds([item.id]); } break; default: setSelectedIds([]); } }