diff --git a/web/src/app/sok/page.tsx b/web/src/app/sok/page.tsx index e800f5e..73d7b07 100644 --- a/web/src/app/sok/page.tsx +++ b/web/src/app/sok/page.tsx @@ -1,10 +1,10 @@ -import { graphql } from "@/gql"; import { getClient } from "@/app/client"; import { - SearchContainer, type SearchResult, -} from "@/components/search/SearchContainer"; -import { Suspense } from "react"; + SearchResults, +} from "@/components/search/SearchResults"; +import { SearchShell } from "@/components/search/SearchShell"; +import { graphql } from "@/gql"; // TODO: seo metadata? @@ -69,9 +69,9 @@ export default async function Page({ return ( - - - + + {query ? : null} + ); } diff --git a/web/src/components/search/SearchContainer.tsx b/web/src/components/search/SearchResults.tsx similarity index 72% rename from web/src/components/search/SearchContainer.tsx rename to web/src/components/search/SearchResults.tsx index 5d3ffee..3450702 100644 --- a/web/src/components/search/SearchContainer.tsx +++ b/web/src/components/search/SearchResults.tsx @@ -1,18 +1,8 @@ -"use client"; -import { useEffect, useState } from "react"; -import { useDebouncedCallback } from "use-debounce"; -import { PageHeader } from "../general/PageHeader"; -import { useRouter } from "next/navigation"; -import { - ImageFragmentDefinition, - getSearchPath, - stripHtml, -} from "@/lib/common"; +import { ImageFragmentDefinition, stripHtml } from "@/lib/common"; import { formatDate, formatOccurrenceMonths } from "@/lib/date"; import { unmaskFragment } from "@/gql"; import type { ImageFragment, SearchQuery } from "@/gql/graphql"; import styles from "./searchContainer.module.scss"; -import { Icon } from "../general/Icon"; import { Image } from "../general/Image"; import Link from "next/link"; @@ -29,57 +19,12 @@ const PAGE_TYPES = { type SupportedTypename = keyof typeof PAGE_TYPES; type SupportedResult = Extract; -export function SearchContainer({ - query, - results, -}: { - query: string; - results: SearchResult[]; -}) { - const { replace } = useRouter(); - const [inputValue, setInputValue] = useState(query); - - useEffect(() => { - setInputValue(query); - }, [query]); - - const pushQuery = useDebouncedCallback((next: string) => { - replace(getSearchPath(next)); - }, 500); - - return ( - - - - { - setInputValue(e.target.value); - pushQuery(e.target.value); - }} - /> - - - - - {query && } - - ); -} - function capitalizeFirstLetter(s: string) { return s.charAt(0).toUpperCase() + s.slice(1); } function isSupported(result: SearchResult): result is SupportedResult { - return ( - result.__typename in PAGE_TYPES && - "id" in result && - !!result.id - ); + return result.__typename in PAGE_TYPES && "id" in result && !!result.id; } function getResultType(result: SupportedResult): string { @@ -131,7 +76,7 @@ function getResultSnippet(result: SupportedResult): string | null { } } -function SearchResults({ results }: { results: SearchResult[] }) { +export function SearchResults({ results }: { results: SearchResult[] }) { if (!results.length) { return Ingen resultater; } diff --git a/web/src/components/search/SearchShell.tsx b/web/src/components/search/SearchShell.tsx new file mode 100644 index 0000000..fe09ea6 --- /dev/null +++ b/web/src/components/search/SearchShell.tsx @@ -0,0 +1,57 @@ +"use client"; +import { useEffect, useRef, useState, useTransition, type ReactNode } from "react"; +import { useDebouncedCallback } from "use-debounce"; +import { useRouter } from "next/navigation"; +import { PageHeader } from "../general/PageHeader"; +import { getSearchPath } from "@/lib/common"; +import styles from "./searchContainer.module.scss"; +import { Icon } from "../general/Icon"; + +export function SearchShell({ + initialQuery, + children, +}: { + initialQuery: string; + children: ReactNode; +}) { + const { replace } = useRouter(); + const [inputValue, setInputValue] = useState(initialQuery); + const [, startTransition] = useTransition(); + const lastPushedRef = useRef(initialQuery); + + const pushQuery = useDebouncedCallback((next: string) => { + lastPushedRef.current = next; + startTransition(() => { + replace(getSearchPath(next)); + }); + }, 300); + + useEffect(() => { + if (initialQuery !== lastPushedRef.current) { + lastPushedRef.current = initialQuery; + setInputValue(initialQuery); + } + }, [initialQuery]); + + return ( + + + + { + setInputValue(e.target.value); + pushQuery(e.target.value); + }} + /> + + + + + {children} + + ); +}