import { selector, selectorFamily } from "recoil"; import TinyColor from "tinycolor2"; import Fuse from "fuse.js"; import { searchQueryAtom, iconColorAtom } from "./atoms"; import { IconEntry, IconCategory } from "../lib"; import { icons } from "../lib/icons"; const fuse = new Fuse(icons, { keys: [{ name: "name", weight: 2 }, "tags", "categories"], threshold: 0.2, // Tweak this to what feels like the right number of results // shouldSort: false, // We may want to sort if we find too many results? }); export const filteredQueryResultsSelector = selector>({ key: "filteredQueryResultsSelector", get: ({ get }) => { const query = get(searchQueryAtom).trim().toLowerCase(); if (!query) return icons; return new Promise((resolve) => resolve(fuse.search(query).map((value) => value.item)) ); }, }); type CategorizedIcons = Partial>; export const categorizedQueryResultsSelector = selector< Readonly >({ key: "categorizedQueryResultsSelector", get: ({ get }) => { const filteredResults = get(filteredQueryResultsSelector); return new Promise((resolve) => resolve( filteredResults.reduce((acc, curr) => { curr.categories.forEach((category) => { if (!acc[category]) acc[category] = []; acc[category]!!.push(curr); }); return acc; }, {}) ) ); }, }); export const singleCategoryQueryResultsSelector = selectorFamily< Readonly, IconCategory >({ key: "singleCategoryQueryResultsSelector", get: (category: IconCategory) => ({ get }) => { const filteredResults = get(filteredQueryResultsSelector); return new Promise((resolve) => resolve( filteredResults.filter((icon) => icon.categories.includes(category)) ) ); }, }); export const isDarkThemeSelector = selector({ key: "isDarkThemeSelector", get: ({ get }) => TinyColor(get(iconColorAtom)).isLight(), });