an app to share curated trails
sidetrail.app
1"use client";
2
3import { Suspense } from "react";
4import { useSelectedLayoutSegment } from "next/navigation";
5import Link, { useLinkStatus } from "next/link";
6import "./SegmentTabs.css";
7
8interface SegmentTabsProps {
9 segments: Array<{
10 segment: string | null;
11 title: string;
12 children?: React.ReactNode;
13 href?: string;
14 }>;
15 basePath?: string;
16}
17
18function TabContent({
19 title,
20 children,
21 isActive,
22}: {
23 title: string;
24 children?: React.ReactNode;
25 isActive: boolean;
26}) {
27 const { pending } = useLinkStatus();
28 const className = pending
29 ? "SegmentTabs-tabText--pending"
30 : isActive
31 ? "SegmentTabs-tabText--active"
32 : undefined;
33 return (
34 <span className={className}>
35 {title}
36 {children}
37 </span>
38 );
39}
40
41export function SegmentTabs({ segments, basePath = "/" }: SegmentTabsProps) {
42 return (
43 <Suspense fallback={<SegmentTabsShim segments={segments} basePath={basePath} />}>
44 <SegmentTabsLoaded segments={segments} basePath={basePath} />
45 </Suspense>
46 );
47}
48
49export function SegmentTabsShim({ segments, basePath }: SegmentTabsProps) {
50 return <SegmentTabsBase selected={null} segments={segments} basePath={basePath} />;
51}
52
53export function SegmentTabsLoaded({ segments, basePath }: SegmentTabsProps) {
54 const selected = useSelectedLayoutSegment();
55 return <SegmentTabsBase selected={selected} segments={segments} basePath={basePath} />;
56}
57
58export function SegmentTabsBase({
59 segments,
60 basePath,
61 selected,
62}: SegmentTabsProps & { selected: string | null }) {
63 return (
64 <nav className="SegmentTabs">
65 {segments.map((segment) => {
66 const isActive = selected === segment.segment;
67 return (
68 <Link
69 key={segment.segment}
70 href={segment.href ?? basePath + (segment.segment ?? "")}
71 className={`SegmentTabs-tab ${isActive ? "SegmentTabs-tab--active" : ""}`}
72 >
73 <TabContent title={segment.title} isActive={isActive}>
74 {segment.children}
75 </TabContent>
76 </Link>
77 );
78 })}
79 </nav>
80 );
81}