Skip to content

Commit

Permalink
Add DocSearch by Algolia
Browse files Browse the repository at this point in the history
  • Loading branch information
brookslybrand committed May 13, 2024
1 parent 379cc11 commit 1870ace
Show file tree
Hide file tree
Showing 8 changed files with 599 additions and 9 deletions.
39 changes: 39 additions & 0 deletions app/modules/docsearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { DocSearchProps } from "@docsearch/react";
import "@docsearch/css/dist/style.css";
import "~/styles/docsearch.css";
import { useHydrated } from "~/ui/utils";
import { Suspense, lazy } from "react";

const OriginalDocSearch = lazy(() =>
import("@docsearch/react").then((module) => ({
default: module.DocSearch,
}))
);

let docSearchProps = {
appId: "RB6LOUCOL0",
indexName: "reactrouter",
apiKey: "b50c5d7d9f4610c9785fa945fdc97476",
} satisfies DocSearchProps;

// TODO: Refactor a bit when we add Vite with css imports per component
// This will allow us to have two versions of the component, one that has
// the button with display: none, and the other with button styles
export function DocSearch() {
let hydrated = useHydrated();

if (!hydrated) {
// The Algolia doc search container is hard-coded at 40px. It doesn't
// render anything on the server, so we get a mis-match after hydration.
// This placeholder prevents layout shift when the search appears.
return <div className="h-10" />;
}

return (
<Suspense fallback={<div className="h-10" />}>
<div className="animate-[fadeIn_100ms_ease-in_1]">
<OriginalDocSearch {...docSearchProps} />
</div>
</Suspense>
);
}
4 changes: 3 additions & 1 deletion app/routes/$lang.$ref.$.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const meta: MetaFunction<
root: typeof rootLoader;
"routes/$lang.$ref": typeof langRefLoader;
}
> = ({ data, matches }) => {
> = ({ data, matches, params }) => {
if (!data) return [{ title: "Not Found" }];
let parentMatch = matches.find((m) => m.id === "routes/$lang.$ref");
let parentData = parentMatch ? parentMatch.data : undefined;
Expand Down Expand Up @@ -100,6 +100,8 @@ export const meta: MetaFunction<

return [
...meta,
{ name: "docsearch:language", content: params.lang || "en" },
{ name: "docsearch:version", content: params.ref || "v6" },
{ name: "robots", content: robots },
{ name: "googlebot", content: robots },
];
Expand Down
26 changes: 26 additions & 0 deletions app/routes/$lang.$ref.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { getLatestVersion } from "~/modules/gh-docs/.server/tags";
import { useColorScheme } from "~/modules/color-scheme/components";

import docsStylesheet from "~/styles/docs.css?url";
import { DocSearch } from "~/modules/docsearch";

export let links: LinksFunction = () => {
return [{ rel: "stylesheet", href: docsStylesheet }];
Expand Down Expand Up @@ -180,6 +181,7 @@ function Header() {
<div className="flex items-center gap-2">
<VersionSelect />
<ColorSchemeToggle />
<DocSearchSection className="lg:hidden" />
</div>
</div>
<VersionWarning />
Expand Down Expand Up @@ -209,6 +211,29 @@ function Header() {
);
}

function DocSearchSection({ className }: { className?: string }) {
return (
<div
className={classNames(
"relative -mx-3 lg:sticky lg:top-0 lg:z-10",
className
)}
>
<div className="absolute -top-24 hidden h-24 w-full bg-white dark:bg-gray-900 lg:block" />
<div
className={classNames(
"relative lg:bg-white lg:dark:bg-gray-900",
// This hides some of the underlying text when the user scrolls to the
// bottom which results in the overscroll bounce
"before:absolute before:bottom-0 before:left-0 before:-z-10 before:hidden before:h-[200%] before:w-full before:bg-inherit lg:before:block"
)}
>
<DocSearch />
</div>
</div>
);
}

function ColorSchemeToggle() {
let location = useLocation();

Expand Down Expand Up @@ -329,6 +354,7 @@ function NavMenuDesktop() {
"h-[calc(100vh-var(--header-height))]"
)}
>
<DocSearchSection />
<div className="[&_*:focus]:scroll-mt-[6rem]">
<Menu />
</div>
Expand Down
58 changes: 58 additions & 0 deletions app/styles/docsearch.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* this was copy/paste/modified from @docsearch/css/dist/_variables.css */
:root {
&:where(.dark) {
--docsearch-text-color: #f5f6f7;
--docsearch-container-background: rgba(9, 10, 17, 0.8);
--docsearch-modal-background: #15172a;
--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
--docsearch-searchbox-background: #090a11;
--docsearch-searchbox-focus-background: #000;
--docsearch-hit-color: #bec3c9;
--docsearch-hit-shadow: none;
--docsearch-hit-background: #090a11;
--docsearch-key-gradient: linear-gradient(-26.5deg, #565872, #31355b);
--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
0 2px 2px 0 rgba(3, 4, 9, 0.3);
--docsearch-footer-background: #1e2136;
--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5),
0 -4px 8px 0 rgba(0, 0, 0, 0.2);
--docsearch-logo-color: #fff;
--docsearch-muted-color: #7f8497;
}
}

.DocSearch-Button {
@apply ml-0 h-10 bg-gray-100 px-3 hover:bg-gray-200 hover:shadow-none focus:bg-gray-200 focus-visible:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus-visible:bg-gray-700;

@media screen and (min-width: theme(screens.lg)) {
width: 100%;
}
@media not screen and (min-width: theme(screens.lg)) {
@apply w-10 place-content-center px-0;
.DocSearch-Button-Keys,
.DocSearch-Button-Placeholder {
display: none;
}
}
@media print {
display: none;
}
}

.DocSearch-Button-Placeholder {
@apply text-sm font-normal text-black dark:text-gray-200;
}

.DocSearch-Button-Keys {
@apply justify-end gap-1;
}

.DocSearch-Button-Key {
all: unset;

@apply grid h-5 w-3.5 place-items-center rounded bg-white px-1 text-xs text-gray-600 dark:bg-gray-600 dark:text-gray-200;
}

.DocSearch-Container {
cursor: auto;
}
12 changes: 12 additions & 0 deletions app/ui/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useEffect, useState } from "react";

let hydrating = true;

export function useHydrated() {
let [hydrated, setHydrated] = useState(() => !hydrating);
useEffect(() => {
hydrating = false;
setHydrated(true);
}, []);
return hydrated;
}
Loading

0 comments on commit 1870ace

Please sign in to comment.