Extract documents page layout into a tv variant

The results container hand-wrote its layout classes and toggled the
dimmed state with a string-interpolated conditional. Move both the page
shell and the busy/dimmed state into a documentsLayout tv variant, and
reuse it in the skeleton so the loading and loaded layouts share one
source of truth.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-07-15 19:11:27 +02:00
parent e546f24148
commit bce75355c0
3 changed files with 50 additions and 7 deletions

View File

@@ -38,6 +38,7 @@ import { TrustCenterFileListItem } from "./_components/TrustCenterFileListItem";
import { groupByField } from "./_lib/groupByField";
import { toQueryVariables } from "./_lib/toQueryVariables";
import { useDocumentTab } from "./_lib/useDocumentTab";
import { documentsLayout } from "./variants";
export const documentsPageQuery = graphql`
query DocumentsPageQuery($visibility: TrustCenterVisibility) {
@@ -129,16 +130,15 @@ export function DocumentsPage({ queryRef }: DocumentsPageProps) {
const fileGroups = groupByField(fileNodes, node => node.category)
.sort((a, b) => a.key.localeCompare(b.key));
const { page, results } = documentsLayout({ busy: isRefetching });
return (
<>
<PageHeader title={t("title")} count={total} flushBottomSpace>
<DocumentsToolbar />
</PageHeader>
<div className="flex w-full flex-col items-center px-8 py-8">
<div
aria-busy={isRefetching}
className={`flex w-full max-w-5xl flex-col gap-8 transition-opacity duration-150 ${isRefetching ? "opacity-60" : ""}`}
>
<div className={page()}>
<div aria-busy={isRefetching} className={results()}>
<ListErrorBoundary
onRetry={done => startTransition(() => {
refetch(toQueryVariables(tab), { fetchPolicy: "network-only", onComplete: done });

View File

@@ -24,10 +24,14 @@ import { TextSkeleton } from "@probo/ui/src/v2/typography/TextSkeleton";
import { HeaderBand } from "#/components/HeaderBand/HeaderBand";
import { documentsLayout } from "./variants";
const SECTION_PLACEHOLDERS = ["a", "b"];
const ROW_PLACEHOLDERS = ["x", "y", "z"];
export function DocumentsPageSkeleton() {
const { page, results } = documentsLayout();
return (
<>
<HeaderBand flushBottomSpace>
@@ -36,8 +40,8 @@ export function DocumentsPageSkeleton() {
<TabsSkeleton />
</div>
</HeaderBand>
<div className="flex w-full flex-col items-center px-8 py-8">
<div className="flex w-full max-w-5xl flex-col gap-8">
<div className={page()}>
<div className={results()}>
{SECTION_PLACEHOLDERS.map(section => (
<div key={section} className="flex flex-col gap-3">
<TextSkeleton size={3} className="w-40" />

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { tv } from "tailwind-variants/lite";
// Documents page shell: a centered content column below the header band. The
// `busy` variant dims the current results while a filtered slice refetches.
export const documentsLayout = tv({
slots: {
page: "flex w-full flex-col items-center px-8 py-8",
results: "flex w-full max-w-5xl flex-col gap-8 transition-opacity duration-150",
},
variants: {
busy: {
true: { results: "opacity-60" },
false: {},
},
},
defaultVariants: {
busy: false,
},
});