diff --git a/compass/components/Table/Index.tsx b/compass/components/Table/Index.tsx index 2b02c16..10f81ed 100644 --- a/compass/components/Table/Index.tsx +++ b/compass/components/Table/Index.tsx @@ -3,16 +3,20 @@ import usersImport from "./users.json"; import { ColumnDef, + Row, createColumnHelper, flexRender, getCoreRowModel, + getFilteredRowModel, + sortingFns, useReactTable, } from "@tanstack/react-table"; -import { useState, useEffect } from "react"; +import { ChangeEvent, useState, useEffect } from "react"; import { RowOptionMenu } from "./RowOptionMenu"; import { RowOpenAction } from "./RowOpenAction"; import { TableAction } from "./TableAction"; import { Bars2Icon, AtSymbolIcon, HashtagIcon, ArrowDownCircleIcon } from "@heroicons/react/24/solid"; +import { rankItem } from "@tanstack/match-sorter-utils"; const usersExample = usersImport as unknown as User[]; @@ -28,6 +32,17 @@ type User = { visible: boolean; }; +// For search +const fuzzyFilter = (row: Row, columnId: string, value: any, addMeta: (meta: any) => void) => { + // Rank the item + const itemRank = rankItem(row.getValue(columnId), value) + + // Store the ranking info + addMeta(itemRank) + + // Return if the item should be filtered in/out + return itemRank.passed +} export const Table = () => { const columnHelper = createColumnHelper(); @@ -43,7 +58,7 @@ export const Table = () => { }; const hideUser = (userId: number) => { - console.log(`Toggling visibility for user with ID: ${userId}`); + console.log(`Toggling visibility for user with ID: ${userId}`); setData(currentData => { const newData = currentData.map(user => { if (user.id === userId) { @@ -51,12 +66,12 @@ export const Table = () => { } return user; }).sort((a, b) => a.visible === b.visible ? 0 : a.visible ? -1 : 1); - - console.log(newData); + + console.log(newData); return newData; }); }; - + const columns = [ columnHelper.display({ id: "options", @@ -82,16 +97,35 @@ export const Table = () => { const [data, setData] = useState([...usersExample]); + // Searching + const [query, setQuery] = useState(""); + const handleSearchChange = (e: ChangeEvent) => { + const target = e.target as HTMLInputElement; + setQuery(String(target.value)); + } + + // TODO: Filtering + // TODO: Sorting + const table = useReactTable({ columns, data, + filterFns: { + fuzzy: fuzzyFilter + }, + state: { + globalFilter: query, + }, + onGlobalFilterChange: setQuery, + globalFilterFn: fuzzyFilter, getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), }); return (
- +
diff --git a/compass/components/Table/TableAction.tsx b/compass/components/Table/TableAction.tsx index f2a72a0..e58e110 100644 --- a/compass/components/Table/TableAction.tsx +++ b/compass/components/Table/TableAction.tsx @@ -1,9 +1,16 @@ +/** The actions (Filter, Sort, Search) at the top of the table. */ import { MagnifyingGlassIcon } from "@heroicons/react/24/solid"; -import { useRef, useState } from "react"; +import { ChangeEventHandler, Dispatch, FunctionComponent, SetStateAction, useRef, useState } from "react"; -export const TableAction = () => { +type TableActionProps = { + query: string + handleChange: ChangeEventHandler +} + +export const TableAction: FunctionComponent = ({query, handleChange}) => { const searchInput = useRef(null); const [searchActive, setSearchActive] = useState(false); + const activateSearch = () => { setSearchActive(true); if (searchInput.current === null) { return; } @@ -30,7 +37,10 @@ export const TableAction = () => { className={"outline-none transition-all duration-300 " + (searchActive ? "w-48" : "w-0")} type="text" name="search" - placeholder="Type to search..." /> + placeholder="Type to search..." + value={query ?? ""} + onChange={handleChange} + /> ); }; diff --git a/compass/package-lock.json b/compass/package-lock.json index fd7ab14..f37a923 100644 --- a/compass/package-lock.json +++ b/compass/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@heroicons/react": "^2.1.1", + "@tanstack/match-sorter-utils": "^8.15.1", "@tanstack/react-table": "^8.15.0", "next": "13.5.6", "react": "^18", @@ -402,6 +403,21 @@ "tslib": "^2.4.0" } }, + "node_modules/@tanstack/match-sorter-utils": { + "version": "8.15.1", + "resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.15.1.tgz", + "integrity": "sha512-PnVV3d2poenUM31ZbZi/yXkBu3J7kd5k2u51CGwwNojag451AjTH9N6n41yjXz2fpLeewleyLBmNS6+HcGDlXw==", + "dependencies": { + "remove-accents": "0.5.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tanstack/react-table": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.15.0.tgz", @@ -3556,6 +3572,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", diff --git a/compass/package.json b/compass/package.json index 4036d60..10ba9d0 100644 --- a/compass/package.json +++ b/compass/package.json @@ -1,4 +1,3 @@ - { "name": "compass", "version": "0.1.0", @@ -11,6 +10,7 @@ }, "dependencies": { "@heroicons/react": "^2.1.1", + "@tanstack/match-sorter-utils": "^8.15.1", "@tanstack/react-table": "^8.15.0", "next": "13.5.6", "react": "^18",