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:
@@ -38,6 +38,7 @@ import { TrustCenterFileListItem } from "./_components/TrustCenterFileListItem";
|
|||||||
import { groupByField } from "./_lib/groupByField";
|
import { groupByField } from "./_lib/groupByField";
|
||||||
import { toQueryVariables } from "./_lib/toQueryVariables";
|
import { toQueryVariables } from "./_lib/toQueryVariables";
|
||||||
import { useDocumentTab } from "./_lib/useDocumentTab";
|
import { useDocumentTab } from "./_lib/useDocumentTab";
|
||||||
|
import { documentsLayout } from "./variants";
|
||||||
|
|
||||||
export const documentsPageQuery = graphql`
|
export const documentsPageQuery = graphql`
|
||||||
query DocumentsPageQuery($visibility: TrustCenterVisibility) {
|
query DocumentsPageQuery($visibility: TrustCenterVisibility) {
|
||||||
@@ -129,16 +130,15 @@ export function DocumentsPage({ queryRef }: DocumentsPageProps) {
|
|||||||
const fileGroups = groupByField(fileNodes, node => node.category)
|
const fileGroups = groupByField(fileNodes, node => node.category)
|
||||||
.sort((a, b) => a.key.localeCompare(b.key));
|
.sort((a, b) => a.key.localeCompare(b.key));
|
||||||
|
|
||||||
|
const { page, results } = documentsLayout({ busy: isRefetching });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHeader title={t("title")} count={total} flushBottomSpace>
|
<PageHeader title={t("title")} count={total} flushBottomSpace>
|
||||||
<DocumentsToolbar />
|
<DocumentsToolbar />
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<div className="flex w-full flex-col items-center px-8 py-8">
|
<div className={page()}>
|
||||||
<div
|
<div aria-busy={isRefetching} className={results()}>
|
||||||
aria-busy={isRefetching}
|
|
||||||
className={`flex w-full max-w-5xl flex-col gap-8 transition-opacity duration-150 ${isRefetching ? "opacity-60" : ""}`}
|
|
||||||
>
|
|
||||||
<ListErrorBoundary
|
<ListErrorBoundary
|
||||||
onRetry={done => startTransition(() => {
|
onRetry={done => startTransition(() => {
|
||||||
refetch(toQueryVariables(tab), { fetchPolicy: "network-only", onComplete: done });
|
refetch(toQueryVariables(tab), { fetchPolicy: "network-only", onComplete: done });
|
||||||
|
|||||||
@@ -24,10 +24,14 @@ import { TextSkeleton } from "@probo/ui/src/v2/typography/TextSkeleton";
|
|||||||
|
|
||||||
import { HeaderBand } from "#/components/HeaderBand/HeaderBand";
|
import { HeaderBand } from "#/components/HeaderBand/HeaderBand";
|
||||||
|
|
||||||
|
import { documentsLayout } from "./variants";
|
||||||
|
|
||||||
const SECTION_PLACEHOLDERS = ["a", "b"];
|
const SECTION_PLACEHOLDERS = ["a", "b"];
|
||||||
const ROW_PLACEHOLDERS = ["x", "y", "z"];
|
const ROW_PLACEHOLDERS = ["x", "y", "z"];
|
||||||
|
|
||||||
export function DocumentsPageSkeleton() {
|
export function DocumentsPageSkeleton() {
|
||||||
|
const { page, results } = documentsLayout();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeaderBand flushBottomSpace>
|
<HeaderBand flushBottomSpace>
|
||||||
@@ -36,8 +40,8 @@ export function DocumentsPageSkeleton() {
|
|||||||
<TabsSkeleton />
|
<TabsSkeleton />
|
||||||
</div>
|
</div>
|
||||||
</HeaderBand>
|
</HeaderBand>
|
||||||
<div className="flex w-full flex-col items-center px-8 py-8">
|
<div className={page()}>
|
||||||
<div className="flex w-full max-w-5xl flex-col gap-8">
|
<div className={results()}>
|
||||||
{SECTION_PLACEHOLDERS.map(section => (
|
{SECTION_PLACEHOLDERS.map(section => (
|
||||||
<div key={section} className="flex flex-col gap-3">
|
<div key={section} className="flex flex-col gap-3">
|
||||||
<TextSkeleton size={3} className="w-40" />
|
<TextSkeleton size={3} className="w-40" />
|
||||||
|
|||||||
39
apps/compliance-portal/src/pages/documents/variants.ts
Normal file
39
apps/compliance-portal/src/pages/documents/variants.ts
Normal 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,
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user