Skip to content

Commit

Permalink
style: make inner content scrollable on all pages
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinstadler committed Dec 10, 2024
1 parent edc37e9 commit 8aa24b8
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 57 deletions.
42 changes: 22 additions & 20 deletions app/[locale]/search/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function FilterMenu(props: {
export default function SearchPage() {
const tl = useTranslations("Languages");
return (
<MainContent className="mx-auto w-screen max-w-screen-lg p-6">
<MainContent className="mx-auto h-full w-screen max-w-screen-lg p-6">
<InstantSearchView
queryArgsToMenuFields={{
// the order of elements here determines the order of refinement lists in the UI
Expand All @@ -60,28 +60,30 @@ export default function SearchPage() {
translator: "contains.translators.name" as const,
}}
>
<FilterMenu attribute="contains.work.short_title" />
<div className="absolute h-full overflow-y-auto pr-5">
<FilterMenu attribute="contains.work.short_title" />

<FilterMenu
attribute="language"
className="lowercase"
transformItems={(items) => {
return items.map((item) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
item.label = tl(item.label as any);
return item;
});
}}
/>
<FilterMenu
attribute="language"
className="lowercase"
transformItems={(items) => {
return items.map((item) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
item.label = tl(item.label as any);
return item;
});
}}
/>

{
// FIXME when changing the query removes a refinement from the list, that refinement
// should become inactive!? otherwise it's not clear that it's still toggled...
// TODO pass transform
// <FilterMenu attribute="contains.work.category" />
}
{
// FIXME when changing the query removes a refinement from the list, that refinement
// should become inactive!? otherwise it's not clear that it's still toggled...
// TODO pass transform
// <FilterMenu attribute="contains.work.category" />
}

<FilterMenu attribute="contains.translators.name" />
<FilterMenu attribute="contains.translators.name" />
</div>
</InstantSearchView>
</MainContent>
);
Expand Down
83 changes: 54 additions & 29 deletions app/[locale]/works/[id_or_category]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
import type { RefinementListItem } from "instantsearch.js/es/connectors/refinement-list/connectRefinementList";
import { useTranslations } from "next-intl";

import { InstantSearchView } from "@/components/instantsearch-view";
import { InstantSearchProvider } from "@/components/instantsearch/instantsearchprovider";
import { Results } from "@/components/instantsearch/results";
import { SingleRefinementDropdown } from "@/components/instantsearch/single-refinement-dropdown";
import { SingleRefinementList } from "@/components/instantsearch/single-refinement-list";
import { MainContent } from "@/components/main-content";
import { SingleRefinementList } from "@/components/single-refinement-list";

interface WorksPageProps {
params: {
Expand All @@ -17,34 +19,57 @@ export default function WorksPage(props: WorksPageProps) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const categoryLabel = ct(props.params.id_or_category as any);
const t = useTranslations("InstantSearch");
const tl = useTranslations("Languages");

return (
<MainContent>
<InstantSearchView
filters={`contains.work.category:${props.params.id_or_category}`}
queryArgsToMenuFields={{ language: "language", work: "contains.work.yeartitle" }}
refinementDropdowns={{ language: `${t("all")} ${t("filter_by.language")}` }}
>
<SingleRefinementList
allLabel={categoryLabel}
attribute={"contains.work.yeartitle"}
// format as title (year) instead of showing facet count
refinementArgs={{
// workaround like https://github.com/algolia/instantsearch/issues/2568
transformItems: (items: Array<RefinementListItem>) => {
return items
.filter((item) => {
return item.label.startsWith(props.params.id_or_category);
})
.map((item) => {
const [_cat, year, title] = item.label.split("$");
item.label = Number.isNaN(parseInt(year!)) ? title! : `${title!} (${year!})`;
return item;
});
},
}}
/>
</InstantSearchView>
</MainContent>
<InstantSearchProvider
filters={`contains.work.category:${props.params.id_or_category}`}
queryArgsToMenuFields={{ language: "language", work: "contains.work.yeartitle" }}
>
<MainContent>
<div className="grid h-full grid-cols-[25%_75%] p-2">
<div className="relative h-full">
<SingleRefinementList
allLabel={categoryLabel}
attribute={"contains.work.yeartitle"}
// format as title (year) instead of showing facet count
refinementArgs={{
// workaround like https://github.com/algolia/instantsearch/issues/2568
transformItems: (items: Array<RefinementListItem>) => {
return items
.filter((item) => {
return item.label.startsWith(props.params.id_or_category);
})
.map((item) => {
const [_cat, year, title] = item.label.split("$");
item.label = Number.isNaN(parseInt(year!)) ? title! : `${title!} (${year!})`;
return item;
});
},
}}
/>
</div>
<Results className="h-full overflow-y-auto">
<SingleRefinementDropdown
allLabel={`${t("all")} ${t("filter_by.language")}`}
attribute={"language"}
refinementArgs={{
transformItems: (items: Array<RefinementListItem>) => {
return items
.map((item) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
item.label = `${tl(item.label as any).toLowerCase()} (${item.count.toString()})`;
return item;
})
.sort((a, b) => {
return a.label.localeCompare(b.label);
});
},
}}
/>
</Results>
</div>
</MainContent>
</InstantSearchProvider>
);
}
10 changes: 6 additions & 4 deletions components/instantsearch-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ export function InstantSearchView(props: InstantSearchProps): ReactNode {
filters={props.filters}
queryArgsToMenuFields={props.queryArgsToMenuFields}
>
<div className="grid grid-cols-[25%_75%] p-2">
<div className="mr-10">{props.children}</div>
<div>
<div className="grid h-full grid-cols-[25%_75%] p-2">
<div className="relative mr-10 h-full">{props.children}</div>
<div className="h-full">
<div className="flex place-content-between items-center">
<InstantSearchStats />
<SearchBox placeholder={t("query_placeholder")} />
Expand Down Expand Up @@ -61,7 +61,9 @@ export function InstantSearchView(props: InstantSearchProps): ReactNode {
<span className="sr-only">{t("view.table")}</span>
</Switch>
</div>
{view === "covers" ? <InfiniteScroll /> : <PaginatedTable />}
<div className="relative h-full">
{view === "covers" ? <InfiniteScroll /> : <PaginatedTable />}
</div>
</div>
</div>
</InstantSearchProvider>
Expand Down
4 changes: 2 additions & 2 deletions components/instantsearch/infinitescroll.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function InfiniteScroll(): ReactNode {
}, [isLastPage, showMore]);

return (
<>
<div className="absolute grid h-full grid-rows-[1fr_auto] overflow-y-auto">
<PublicationGrid publications={items} />
{isLastPage ? (
<hr className="m-auto mt-8 w-1/3" />
Expand All @@ -50,6 +50,6 @@ export function InfiniteScroll(): ReactNode {
</Button>
</div>
)}
</>
</div>
);
}
4 changes: 3 additions & 1 deletion components/instantsearch/paginated-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,7 @@ function TableRow({ hit }: { hit: Hit<Publication> }) {
}

export function PaginatedTable(): ReactNode {
return <Hits hitComponent={TableRow} />;
return (
<Hits classNames={{ root: "absolute h-full overflow-y-auto w-full" }} hitComponent={TableRow} />
);
}
47 changes: 47 additions & 0 deletions components/instantsearch/results.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"use client";

import { LayoutGrid, LayoutList } from "lucide-react";
import { useTranslations } from "next-intl";
import { type ReactNode, useState } from "react";
import { Switch } from "react-aria-components";
import { SearchBox } from "react-instantsearch";

import { InfiniteScroll } from "./infinitescroll";
import { PaginatedTable } from "./paginated-table";
import { InstantSearchSortBy } from "./sortby";
import { InstantSearchStats } from "./stats";

interface ResultsProps {
className?: string;
children?: ReactNode;
}

export function Results(props: ResultsProps): ReactNode {
const t = useTranslations("InstantSearch");

// TODO encode current state in URL
const [view, setView] = useState<"covers" | "detail">("covers");

return (
<div className="grid h-full grid-rows-[auto_1fr] overflow-y-auto">
<div className="flex place-content-between items-center">
<InstantSearchStats />
<SearchBox placeholder={t("query_placeholder")} />
<InstantSearchSortBy sortOptions={["year:asc", "year:desc", "title:asc"]} />
{props.children}
<Switch
isSelected={view === "detail"}
onChange={(isSelected) => {
setView(isSelected ? "detail" : "covers");
}}
>
<LayoutGrid size={18} />
<div className="indicator" />
<LayoutList size={18} />
<span className="sr-only">{t("view.table")}</span>
</Switch>
</div>
<div className="relative">{view === "covers" ? <InfiniteScroll /> : <PaginatedTable />}</div>
</div>
);
}
2 changes: 1 addition & 1 deletion components/instantsearch/single-refinement-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function SingleRefinementList(props: SingleRefinementListProps) {
});

return (
<div className="mr-10 grid h-full grid-rows-[auto_1fr]">
<div className="absolute mr-10 grid h-full grid-rows-[auto_1fr] overflow-y-auto">
{props.allLabel ? (
<div className="px-2">
<label
Expand Down

0 comments on commit 8aa24b8

Please sign in to comment.