️ Faster tab switches (#1701)

This commit is contained in:
Luke Vella 2025-04-29 22:34:52 +01:00 committed by GitHub
parent 486bd50139
commit 590e19ae16
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 59 additions and 29 deletions

View file

@ -1,10 +1,14 @@
{
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.importModuleSpecifier": "shortest",
"cSpell.words": ["Rallly", "Vella"],
@ -14,5 +18,6 @@
"typescript.preferences.preferTypeOnlyAutoImports": true,
"typescript.tsserver.log": "verbose",
"typescript.tsserver.trace": "messages",
"references.preferredLocation": "view"
"references.preferredLocation": "view",
"editor.defaultFormatter": "biomejs.biome"
}

View file

@ -1,4 +1,5 @@
"use client";
import { cn } from "@rallly/ui";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@rallly/ui/page-tabs";
import { useRouter, useSearchParams } from "next/navigation";
import React from "react";
@ -9,6 +10,8 @@ export function EventsTabbedView({ children }: { children: React.ReactNode }) {
const searchParams = useSearchParams();
const name = "status";
const router = useRouter();
const [isPending, startTransition] = React.useTransition();
const [tab, setTab] = React.useState(searchParams.get(name) ?? "upcoming");
const handleTabChange = React.useCallback(
(value: string) => {
const params = new URLSearchParams(searchParams);
@ -17,15 +20,16 @@ export function EventsTabbedView({ children }: { children: React.ReactNode }) {
params.delete("page");
const newUrl = `?${params.toString()}`;
router.replace(newUrl, { scroll: false });
startTransition(() => {
setTab(value);
router.replace(newUrl, { scroll: false });
});
},
[router, searchParams],
);
const value = searchParams.get(name) ?? "upcoming";
return (
<Tabs value={value} onValueChange={handleTabChange}>
<Tabs value={tab} onValueChange={handleTabChange}>
<TabsList>
<TabsTrigger value="upcoming">
<Trans i18nKey="upcoming" defaults="Upcoming" />
@ -34,7 +38,15 @@ export function EventsTabbedView({ children }: { children: React.ReactNode }) {
<Trans i18nKey="past" defaults="Past" />
</TabsTrigger>
</TabsList>
<TabsContent tabIndex={-1} value={value} key={value}>
<TabsContent
tabIndex={-1}
value={tab}
key={tab}
className={cn(
"transition-opacity",
isPending ? "opacity-50 delay-200 pointer-events-none" : "",
)}
>
{children}
</TabsContent>
</Tabs>

View file

@ -138,13 +138,13 @@ export default async function Page({
</PageDescription>
</PageHeader>
<PageContent>
<EventsTabbedView>
<div className="space-y-4">
<SearchInput
placeholder={t("searchEventsPlaceholder", {
defaultValue: "Search events by title...",
})}
/>
<div className="space-y-4">
<SearchInput
placeholder={t("searchEventsPlaceholder", {
defaultValue: "Search events by title...",
})}
/>
<EventsTabbedView>
<div className="space-y-6">
{paginatedEvents.length === 0 && (
<ScheduledEventEmptyState status={status} />
@ -177,8 +177,8 @@ export default async function Page({
/>
)}
</div>
</div>
</EventsTabbedView>
</EventsTabbedView>
</div>
</PageContent>
</PageContainer>
);

View file

@ -168,13 +168,13 @@ export default async function Page({
</div>
</div>
<PageContent className="space-y-4">
<SearchInput
placeholder={t("searchPollsPlaceholder", {
defaultValue: "Search polls by title...",
})}
/>
<PollsTabbedView>
<div className="space-y-4">
<SearchInput
placeholder={t("searchPollsPlaceholder", {
defaultValue: "Search polls by title...",
})}
/>
{polls.length === 0 ? (
<PollsEmptyState />
) : (

View file

@ -1,14 +1,18 @@
"use client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@rallly/ui/page-tabs";
import { useRouter, useSearchParams } from "next/navigation";
import React from "react";
import { Trans } from "@/components/trans";
import { cn } from "@rallly/ui";
import React from "react";
export function PollsTabbedView({ children }: { children: React.ReactNode }) {
const searchParams = useSearchParams();
const name = "status";
const router = useRouter();
const [isPending, startTransition] = React.useTransition();
const [tab, setTab] = React.useState(searchParams.get(name) ?? "live");
const handleTabChange = React.useCallback(
(value: string) => {
const params = new URLSearchParams(searchParams);
@ -16,16 +20,17 @@ export function PollsTabbedView({ children }: { children: React.ReactNode }) {
params.delete("page");
const newUrl = `?${params.toString()}`;
router.replace(newUrl, { scroll: false });
startTransition(() => {
setTab(value);
const newUrl = `?${params.toString()}`;
router.replace(newUrl, { scroll: false });
});
},
[router, searchParams],
);
const value = searchParams.get(name) ?? "live";
return (
<Tabs value={value} onValueChange={handleTabChange}>
<Tabs value={tab} onValueChange={handleTabChange}>
<TabsList>
<TabsTrigger value="live">
<Trans i18nKey="pollStatusOpen" defaults="Live" />
@ -37,7 +42,15 @@ export function PollsTabbedView({ children }: { children: React.ReactNode }) {
<Trans i18nKey="pollStatusFinalized" defaults="Finalized" />
</TabsTrigger>
</TabsList>
<TabsContent tabIndex={-1} value={value} key={value}>
<TabsContent
tabIndex={-1}
value={tab}
key={tab}
className={cn(
"transition-opacity",
isPending ? "opacity-50 delay-200 pointer-events-none" : "",
)}
>
{children}
</TabsContent>
</Tabs>

View file

@ -18,7 +18,7 @@ const Progress = React.forwardRef<
{...props}
>
<ProgressPrimitive.Indicator
className="bg-primary h-full w-full flex-1 transition-all"
className="bg-gradient-to-r from-purple-500 to-indigo-500 h-full w-full flex-1 transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>