diff --git a/package-lock.json b/package-lock.json index 85a7b0d..2e62a1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@radix-ui/react-dialog": "^1.0.5", "clsx": "^2.1.0", + "fuse.js": "^7.0.0", "gray-matter": "^4.0.3", "highlight.js": "^11.9.0", "marked": "^12.0.0", @@ -4292,6 +4293,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fuse.js": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz", + "integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==", + "engines": { + "node": ">=10" + } + }, "node_modules/get-east-asian-width": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", diff --git a/package.json b/package.json index 006465e..1293554 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "dependencies": { "@radix-ui/react-dialog": "^1.0.5", "clsx": "^2.1.0", + "fuse.js": "^7.0.0", "gray-matter": "^4.0.3", "highlight.js": "^11.9.0", "marked": "^12.0.0", diff --git a/src/components/ItemList/ItemList.module.css b/src/components/ItemList/ItemList.module.css index d986332..9c41190 100644 --- a/src/components/ItemList/ItemList.module.css +++ b/src/components/ItemList/ItemList.module.css @@ -18,6 +18,7 @@ .ring { flex: 0 0 auto; + margin-left: 16px; align-self: baseline; } @@ -58,11 +59,10 @@ .link { display: flex; flex-wrap: wrap; - padding: 16px; } .quadrant { - margin: 0 16px 0 auto; + margin-left: auto; } @media (min-width: 768px) { diff --git a/src/components/ItemList/ItemList.tsx b/src/components/ItemList/ItemList.tsx index 5c94643..5c3cc0e 100644 --- a/src/components/ItemList/ItemList.tsx +++ b/src/components/ItemList/ItemList.tsx @@ -11,6 +11,7 @@ export interface ItemListProps { items: Item[]; activeId?: string; size?: "small" | "default" | "large"; + hideRing?: boolean; className?: string; } @@ -18,6 +19,7 @@ export function ItemList({ items, activeId, size = "default", + hideRing = false, className, }: ItemListProps) { return ( @@ -48,11 +50,13 @@ export function ItemList({ {getQuadrant(item.quadrant)?.title} - + {!hideRing && ( + + )} )} diff --git a/src/pages/overview.tsx b/src/pages/overview.tsx index 8497536..0b8c06a 100644 --- a/src/pages/overview.tsx +++ b/src/pages/overview.tsx @@ -1,3 +1,4 @@ +import Fuse from "fuse.js"; import Head from "next/head"; import { useRouter } from "next/router"; import { useCallback, useMemo } from "react"; @@ -11,7 +12,7 @@ import { CustomPage } from "@/pages/_app"; const Overview: CustomPage = () => { const router = useRouter(); const ring = router.query.ring as string | undefined; - const query = router.query.query as string | undefined; + const query = (router.query.query as string) || ""; const onRingChange = useCallback( (ring: string) => { @@ -27,15 +28,38 @@ const Overview: CustomPage = () => { [router, ring], ); - const items = useMemo(() => { - if (!ring && !query) return getItems(); - return getItems().filter((item) => { - if (ring && item.ring !== ring) return false; - return !( - query && !item.title.toLowerCase().includes(query.toLowerCase()) - ); + const { items, index } = useMemo(() => { + const items = getItems().filter((item) => !ring || item.ring === ring); + const index = new Fuse(items, { + threshold: 0.3, + includeScore: true, + keys: [ + { + name: "title", + weight: 1.5, + }, + { + name: "tags", + weight: 1, + }, + { + name: "body", + weight: 0.9, + }, + { + name: "revision.body", + weight: 0.7, + }, + ], }); - }, [query, ring]); + + return { items, index }; + }, [ring]); + + const results = useMemo(() => { + if (!query) return items; + return index.search(query).map((result) => result.item); + }, [query, index, items]); return ( <> @@ -51,7 +75,7 @@ const Overview: CustomPage = () => { onQueryChange={onQueryChange} /> - + ); }; diff --git a/src/styles/globals.css b/src/styles/globals.css index c1b3f78..c4eff93 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -95,7 +95,7 @@ input { color: var(--background); border: 1px solid transparent; padding: 10px 12px; - border-radius: 6px; + border-radius: 3px; width: 100%; font-size: 16px;