Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion next-frontend/src/components/competitions/LiveMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default async function LiveMenu({
const tabs = duringCompetitionTabs(competitionInfo, data.rounds);

return (
<TabMenu tabs={tabs} competitionInfo={competitionInfo}>
<TabMenu tabs={tabs} competitionInfo={competitionInfo} isLiveMenu>
{children}
</TabMenu>
);
Expand Down
100 changes: 80 additions & 20 deletions next-frontend/src/components/competitions/TabMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Collapsible,
Drawer,
IconButton,
Separator,
Spacer,
Tabs,
Text,
Expand All @@ -24,25 +25,40 @@ import { useState } from "react";
import { TFunction } from "i18next";
import { LuAlignJustify } from "react-icons/lu";
import { iconMap } from "@/components/icons/iconMap";
import { route } from "nextjs-routes";
import { parseActivityCode } from "@/lib/wca/wcif/rounds";

function parseActivityCodeOrNull(path: string) {
try {
const { eventId } = parseActivityCode(path);
return eventId;
} catch {
return null;
}
}
Comment on lines +31 to +38
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. No try-catch. Not at all.
What is causing parseActivityCode to raise an exception and how can we detect these edge cases before passing the data into parseActivityCode?


export default function TabMenu({
competitionInfo,
children,
tabs,
isLiveMenu = false,
}: {
children: React.ReactNode;
competitionInfo: components["schemas"]["CompetitionInfo"];
tabs: CompetitionNavTab[];
isLiveMenu?: boolean;
}) {
const [openGroup, setOpenGroup] = useState<string | null>(null);
const [drawerOpen, setDrawerOpen] = useState(false);

const pathName = usePathname();
const { t } = useT();

const path = _.last(pathName.split("/"));
const currentPath = path === competitionInfo.id ? "general" : path;

const eventId = parseActivityCodeOrNull(currentPath!);

const [openGroup, setOpenGroup] = useState<string | null>(eventId);
const [drawerOpen, setDrawerOpen] = useState(false);

return (
<Tabs.Root
variant="enclosed"
Expand All @@ -56,7 +72,7 @@ export default function TabMenu({
<Tabs.List
height="fit-content"
position="sticky"
minWidth="fit-content"
width="3xs"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did the previous not work? I think min-width makes a lot of sense with respect to "making sure that long tab titles still at least fit the darn thing", perhaps you want to assing a max-width separately?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because the content size changes when you open the submenus. So the UI gets jumpy. max-width doesn't change anything here

textAlign="center"
hideBelow="md"
gap="3"
Expand All @@ -68,6 +84,8 @@ export default function TabMenu({
onToggle={(tab: CompetitionNavTab) =>
setOpenGroup((prev) => (prev === tab.menuKey ? null : tab.menuKey))
}
isLiveMenu={isLiveMenu}
competitionInfo={competitionInfo}
/>
</Tabs.List>
<Box hideFrom="md" mb="4">
Expand Down Expand Up @@ -117,6 +135,7 @@ export default function TabMenu({
prev === tab.menuKey ? null : tab.menuKey,
)
}
competitionInfo={competitionInfo}
/>
</Tabs.List>
</Drawer.Body>
Expand All @@ -136,28 +155,68 @@ function TabList({
t,
onToggle,
openGroup,
isLiveMenu,
competitionInfo,
}: {
tabs: CompetitionNavTab[];
t: TFunction;
openGroup: string | null;
onToggle: (tab: CompetitionNavTab) => void;
isLiveMenu?: boolean;
competitionInfo: components["schemas"]["CompetitionInfo"];
}) {
return tabs.map((tab) =>
"href" in tab ? (
<Tabs.Trigger value={tab.menuKey} asChild key={tab.menuKey}>
<Text asChild textStyle="bodyEmphasis" justifyContent="left">
<Link href={tab.href}>{t(tab.i18nKey)}</Link>
</Text>
</Tabs.Trigger>
) : (
<CollapsibleTabGroup
key={tab.menuKey}
tab={tab}
t={t}
isOpen={openGroup === tab.menuKey}
onToggle={() => onToggle(tab)}
/>
),
return (
<>
{tabs.map((tab) =>
"href" in tab ? (
<Tabs.Trigger value={tab.menuKey} asChild key={tab.menuKey}>
<Text asChild textStyle="bodyEmphasis" justifyContent="left">
<Link href={tab.href}>{t(tab.i18nKey)}</Link>
</Text>
</Tabs.Trigger>
) : (
<CollapsibleTabGroup
key={tab.menuKey}
tab={tab}
t={t}
isOpen={openGroup === tab.menuKey}
onToggle={() => onToggle(tab)}
/>
),
)}
{!isLiveMenu && (
<>
<Separator />
{competitionInfo.tab_names.map((tabName) => (
<Tabs.Trigger
key={tabName}
value={tabName}
minHeight="fit-content"
asChild
>
<Text
textStyle="bodyEmphasis"
asChild
maxW="44"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did this magical constant come from? I know it is defined in Chakra, but why did you choose it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from the previous version of the menu code that I accidentally deleted. I think it was the most fitting option for custom tabs

justifyContent="left"
>
<Link
href={route({
pathname: "/competitions/[competitionId]/tabs/[tabName]",
query: {
competitionId: competitionInfo.id,
tabName: encodeURIComponent(tabName),
},
})}
>
{tabName}
</Link>
</Text>
</Tabs.Trigger>
))}
</>
)}
</>
);
}

Expand Down Expand Up @@ -185,6 +244,7 @@ function CollapsibleTabGroup({
py="2"
borderRadius="md"
_hover={{ bg: "bg.subtle" }}
cursor="pointer"
>
<Text textStyle="bodyEmphasis">
<IconComponent /> {t(i18nKey)}
Expand Down
7 changes: 6 additions & 1 deletion next-frontend/src/lib/wca/competitions/tabs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,15 @@ export const duringCompetitionTabs = (
rounds.length,
Boolean(round.cutoff),
);

const roundDone =
round.state === "locked" ||
(round.state === "open" &&
round.competitors_live_results_entered === round.total_competitors);
return {
i18nKey: `rounds.${roundTypeId}.name`,
menuKey: round.id,
badge: round.state === "locked" ? "Done" : "live",
badge: roundDone ? "Done" : "live",
disabled: round.state === "pending" || round.state === "ready",
href: route({
pathname: "/competitions/[competitionId]/live/rounds/[roundId]",
Expand Down