mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-10-27 21:18:30 -05:00
Optimize tabs using Activity
This commit is contained in:
@@ -13,6 +13,7 @@ type TabsOrientation = "horizontal" | "vertical";
|
||||
interface TabsContextValue {
|
||||
selectedKey: string | null;
|
||||
orientation: TabsOrientation;
|
||||
keepPanelsMounted: boolean;
|
||||
select: (key: string) => void;
|
||||
registerTab: (key: string, element: HTMLElement) => () => void;
|
||||
moveFocus: (
|
||||
@@ -40,6 +41,8 @@ interface SendouTabsProps {
|
||||
disappearing?: boolean;
|
||||
/** Vertical orientation switches to horizontal below this main content width (px). */
|
||||
horizontalBelow?: number;
|
||||
/** Hidden panels stay mounted via `<Activity>`: pre-rendered in the background once the page is idle and state kept across switches, making tab switches instant. Default false. */
|
||||
keepPanelsMounted?: boolean;
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
@@ -68,6 +71,7 @@ export function SendouTabs({
|
||||
padded = true,
|
||||
disappearing = true,
|
||||
horizontalBelow,
|
||||
keepPanelsMounted = false,
|
||||
className,
|
||||
orientation = "horizontal",
|
||||
children,
|
||||
@@ -112,6 +116,7 @@ export function SendouTabs({
|
||||
const context: TabsContextValue = {
|
||||
selectedKey: currentKey,
|
||||
orientation: effectiveOrientation,
|
||||
keepPanelsMounted,
|
||||
select,
|
||||
registerTab: (key, element) => {
|
||||
tabsRef.current.set(key, element);
|
||||
@@ -326,9 +331,10 @@ export function SendouTabPanel({
|
||||
}: SendouTabPanelProps) {
|
||||
const tabs = useTabsContext();
|
||||
|
||||
if (tabs.selectedKey !== id) return null;
|
||||
const selected = tabs.selectedKey === id;
|
||||
if (!selected && !tabs.keepPanelsMounted) return null;
|
||||
|
||||
return (
|
||||
const panel = (
|
||||
<div
|
||||
className={clsx(className, styles.tabPanel)}
|
||||
role="tabpanel"
|
||||
@@ -338,4 +344,12 @@ export function SendouTabPanel({
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
if (!tabs.keepPanelsMounted) return panel;
|
||||
|
||||
return (
|
||||
<React.Activity mode={selected ? "visible" : "hidden"}>
|
||||
{panel}
|
||||
</React.Activity>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ export function MatchTabs({
|
||||
onSelectionChange={(key) => setTab(key as MatchTabsKey)}
|
||||
disappearing={false}
|
||||
padded={false}
|
||||
keepPanelsMounted
|
||||
>
|
||||
<SendouTabList>
|
||||
{tabs.map((tab) => (
|
||||
|
||||
@@ -85,7 +85,7 @@ export default function TeamIndexPage() {
|
||||
<article data-testid="team-bio">{layoutData.team.bio}</article>
|
||||
) : null}
|
||||
{showSections ? (
|
||||
<SendouTabs>
|
||||
<SendouTabs keepPanelsMounted>
|
||||
<SendouTabList>
|
||||
<SendouTab
|
||||
id="players"
|
||||
|
||||
@@ -237,7 +237,7 @@ function InfoTabs() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SendouTabs>
|
||||
<SendouTabs keepPanelsMounted>
|
||||
<SendouTabList>
|
||||
<SendouTab id="socials" isDisabled={!hasSocials} icon={<LinkIcon />}>
|
||||
{t("org:edit.form.socialLinks.title")}
|
||||
@@ -408,7 +408,7 @@ function SeriesView({
|
||||
<div className="stack md">
|
||||
<SeriesHeader series={series} />
|
||||
<div>
|
||||
<SendouTabs>
|
||||
<SendouTabs keepPanelsMounted>
|
||||
<SendouTabList>
|
||||
<SendouTab id="events" number={series.eventsCount}>
|
||||
{t("org:events.tabs.events")}
|
||||
|
||||
@@ -93,7 +93,7 @@ export default function NewTrophyPage() {
|
||||
|
||||
return (
|
||||
<Main halfWidth>
|
||||
<SendouTabs>
|
||||
<SendouTabs keepPanelsMounted>
|
||||
<SendouTabList>
|
||||
<SendouTab id="upload">{t("trophies:new.tabs.upload")}</SendouTab>
|
||||
<SendouTab
|
||||
|
||||
@@ -87,7 +87,9 @@ class PendingTrophyList {
|
||||
}
|
||||
|
||||
row(trophyName: string) {
|
||||
// the other list stays mounted in a hidden tab panel
|
||||
return this.page
|
||||
.getByRole("tabpanel")
|
||||
.getByTestId("pending-trophy")
|
||||
.filter({ hasText: trophyName });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user