Tyler Hallada
2bd5227287
Also changed how scrolling resets on pagination. And tweaked pagination styling.
126 lines
3.4 KiB
TypeScript
126 lines
3.4 KiB
TypeScript
import React, { useEffect, useRef, useState } from "react";
|
|
import Link from "next/link";
|
|
import MiniSearch from "minisearch";
|
|
import ReactPaginate from "react-paginate";
|
|
|
|
import styles from "../styles/CellList.module.css";
|
|
import type { CellCoord } from "./ModData";
|
|
|
|
const PAGE_SIZE = 100;
|
|
|
|
type Props = {
|
|
cells: CellCoord[];
|
|
};
|
|
|
|
const CellList: React.FC<Props> = ({ cells }) => {
|
|
const cellSearch = useRef<MiniSearch<CellCoord> | null>(
|
|
null
|
|
) as React.MutableRefObject<MiniSearch<CellCoord>>;
|
|
|
|
useEffect(() => {
|
|
cellSearch.current = new MiniSearch({
|
|
fields: ["id"],
|
|
storeFields: ["id", "x", "y"],
|
|
tokenize: (s) => [s.replace(/cell\s?|\s/gi, "")],
|
|
searchOptions: {
|
|
fields: ["id"],
|
|
prefix: true,
|
|
fuzzy: false,
|
|
},
|
|
});
|
|
cellSearch.current.addAll(
|
|
cells.map((cell) => ({ ...cell, id: `${cell.x},${cell.y}` }))
|
|
);
|
|
}, [cells]);
|
|
|
|
const [filter, setFilter] = useState<string>("");
|
|
const [filterResults, setFilterResults] = useState<Set<string>>(new Set());
|
|
const [page, setPage] = useState<number>(0);
|
|
|
|
const filteredCells = cells
|
|
.filter((cell) => !filter || filterResults.has(`${cell.x},${cell.y}`))
|
|
.sort((a, b) => (a.x - b.x) * 1000 + a.y - b.y);
|
|
|
|
useEffect(() => {
|
|
if (cellSearch.current) {
|
|
setFilterResults(
|
|
new Set(cellSearch.current.search(filter).map((result) => result.id))
|
|
);
|
|
}
|
|
}, [filter]);
|
|
|
|
useEffect(() => {
|
|
setPage(0);
|
|
}, [filterResults]);
|
|
|
|
const renderPagination = () => (
|
|
<ReactPaginate
|
|
breakLabel="..."
|
|
nextLabel=">"
|
|
forcePage={page}
|
|
onPageChange={(event) => {
|
|
setPage(event.selected);
|
|
document.getElementById("exterior-cells")?.scrollIntoView();
|
|
}}
|
|
pageRangeDisplayed={3}
|
|
marginPagesDisplayed={2}
|
|
pageCount={Math.ceil(filteredCells.length / PAGE_SIZE)}
|
|
previousLabel="<"
|
|
renderOnZeroPageCount={() => null}
|
|
className={styles.pagination}
|
|
activeClassName={styles["active-page"]}
|
|
hrefBuilder={() => "#"}
|
|
/>
|
|
);
|
|
|
|
return (
|
|
filteredCells && (
|
|
<>
|
|
<h2 id="exterior-cells">Exterior Cells ({filteredCells.length})</h2>
|
|
<div className={styles.filters}>
|
|
<hr />
|
|
<div className={styles["filter-row"]}>
|
|
<label htmlFor="filter">Filter:</label>
|
|
<input
|
|
type="search"
|
|
id="filter"
|
|
className={styles.filter}
|
|
value={filter}
|
|
onChange={(event) => setFilter(event.target.value)}
|
|
/>
|
|
</div>
|
|
<hr />
|
|
</div>
|
|
{renderPagination()}
|
|
<ul className={styles["cell-list"]}>
|
|
{filteredCells
|
|
.slice(page * PAGE_SIZE, page * PAGE_SIZE + PAGE_SIZE)
|
|
.map((cell) => (
|
|
<li
|
|
key={`cell-${cell.x},${cell.y}`}
|
|
className={styles["cell-list-item"]}
|
|
>
|
|
<div className={styles["cell-title"]}>
|
|
<strong>
|
|
<Link
|
|
href={`/?cell=${encodeURIComponent(
|
|
`${cell.x},${cell.y}`
|
|
)}`}
|
|
>
|
|
<a>
|
|
{cell.x}, {cell.y}
|
|
</a>
|
|
</Link>
|
|
</strong>
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
{renderPagination()}
|
|
</>
|
|
)
|
|
);
|
|
};
|
|
|
|
export default CellList;
|