Files
grungnet/src/helpers/select.js

134 lines
3.6 KiB
JavaScript

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([]);
}
}