Stonks/spa/src/Search.js

135 lines
3.8 KiB
JavaScript

import React, { useMemo, useState } from "react";
import Fuse from "fuse.js";
import { Button, Center, Heading, Icon, Input } from "@chakra-ui/react";
import { FaUserCircle, FaArrowAltCircleRight } from "react-icons/fa";
import { Link } from "react-router-dom";
import { AutoSizer, WindowScroller, List } from "react-virtualized";
import prettifyStatisticName from "./PrettifyStatisticName";
import AggregatesShowcase from "./AggregatesShowcase";
const Search = ({ statistics, players }) => {
const [searchTerm, setSearchTerm] = useState("");
const searchEngine = useMemo(() => {
const fullList = statistics
.map((s) => {
return {
type: "statistic",
value: s,
searchTerm: prettifyStatisticName(s.type, s.name),
};
})
.concat(
players.map((p) => {
return {
type: "player",
value: p,
searchTerm: p.name,
};
})
);
return new Fuse(fullList, {
minMatchCharLength: 3,
ignoreLocation: true,
shouldSort: true,
keys: ["searchTerm"],
});
}, [statistics, players]);
const searchResults = useMemo(() => {
return searchEngine.search(`'"${searchTerm}"'`);
}, [searchEngine, searchTerm]);
const renderRow = ({
index, // Index of row
isScrolling, // The List is currently being scrolled
isVisible, // This row is visible within the List (eg it is not an overscanned row)
key, // Unique key within array of rendered rows
parent, // Reference to the parent List (instance)
style, // Style object to be applied to row (to position it);
// This must be passed through to the rendered row element.
}) => {
const x = searchResults[index];
return (
<Link
key={
x.item.type === "player"
? `/${x.item.type}/${x.item.value.id}`
: `/${x.item.type}/${x.item.value.type}/${x.item.value.name}`
}
to={
x.item.type === "player"
? `/${x.item.type}/${x.item.value.id}`
: `/${x.item.type}/${x.item.value.type}/${x.item.value.name}`
}
style={style}
>
<Button
size="sm"
leftIcon={
x.item.type === "player" ? (
<Icon as={FaUserCircle} />
) : (
<Icon as={FaArrowAltCircleRight} transform="rotate(-45deg)" />
)
}
variant="ghost"
my="0.5"
display="block"
textAlign="left"
width="100%"
>
{x.item.searchTerm}
</Button>
</Link>
);
};
return (
<WindowScroller>
{({ height, isScrolling, onChildScroll, scrollTop, registerChild }) => (
<>
<Heading as="h1" size="4xl" my="8">
<Center>📈</Center>
</Heading>
<Input
placeholder="Find statistics or players"
size="lg"
variant="filled"
value={searchTerm}
onChange={(event) => setSearchTerm(event.target.value)}
mb="8"
/>
{searchResults.length === 0 ? <AggregatesShowcase /> : <></>}
<div ref={registerChild}>
{searchResults.length > 0 ? (
<AutoSizer disableHeight>
{({ width }) => (
<List
autoHeight
height={height}
isScrolling={isScrolling}
onScroll={onChildScroll}
scrollTop={scrollTop}
rowCount={searchResults.length}
rowHeight={36}
rowRenderer={renderRow}
width={width}
/>
)}
</AutoSizer>
) : (
<></>
)}
</div>
</>
)}
</WindowScroller>
);
};
export default Search;