Skip to content

Commit 2366b63

Browse files
authored
Merge branch 'main' into nebula-faqs
2 parents 244f1b3 + 826748b commit 2366b63

File tree

42 files changed

+1683
-374
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1683
-374
lines changed

.changeset/crazy-cups-jump.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Fix "Cannot read properties of undefined (reading 'forwardRef')" error in certain bundler setups

apps/dashboard/src/@/components/blocks/MobileSidebar.tsx

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,7 @@ export function MobileSidebar(props: {
1919
triggerClassName?: string;
2020
}) {
2121
const [isOpen, setIsOpen] = useState(false);
22-
const pathname = usePathname();
23-
24-
const activeLink = useMemo(() => {
25-
function isActive(link: SidebarBaseLink) {
26-
if (link.exactMatch) {
27-
return link.href === pathname;
28-
}
29-
return pathname?.startsWith(link.href);
30-
}
31-
32-
for (const link of props.links) {
33-
if ("group" in link) {
34-
for (const subLink of link.links) {
35-
if (isActive(subLink)) {
36-
return subLink;
37-
}
38-
}
39-
} else {
40-
if (isActive(link)) {
41-
return link;
42-
}
43-
}
44-
}
45-
}, [props.links, pathname]);
22+
const activeLink = useActiveSidebarLink(props.links);
4623

4724
const defaultTrigger = (
4825
<Button
@@ -75,3 +52,32 @@ export function MobileSidebar(props: {
7552
</Dialog>
7653
);
7754
}
55+
56+
export function useActiveSidebarLink(links: SidebarLink[]) {
57+
const pathname = usePathname();
58+
59+
const activeLink = useMemo(() => {
60+
function isActive(link: SidebarBaseLink) {
61+
if (link.exactMatch) {
62+
return link.href === pathname;
63+
}
64+
return pathname?.startsWith(link.href);
65+
}
66+
67+
for (const link of links) {
68+
if ("group" in link) {
69+
for (const subLink of link.links) {
70+
if (isActive(subLink)) {
71+
return subLink;
72+
}
73+
}
74+
} else if ("href" in link) {
75+
if (isActive(link)) {
76+
return link;
77+
}
78+
}
79+
}
80+
}, [links, pathname]);
81+
82+
return activeLink;
83+
}

apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,39 @@ export function MultiNetworkSelector(props: {
1818
onChange: (chainIds: number[]) => void;
1919
disableChainId?: boolean;
2020
className?: string;
21+
priorityChains?: number[];
2122
}) {
2223
const { allChains, idToChain } = useAllChainsData();
2324

2425
const options = useMemo(() => {
25-
return allChains.map((chain) => {
26+
let sortedChains = allChains;
27+
28+
if (props.priorityChains) {
29+
const priorityChainsSet = new Set();
30+
for (const chainId of props.priorityChains || []) {
31+
priorityChainsSet.add(chainId);
32+
}
33+
34+
const priorityChains = (props.priorityChains || [])
35+
.map((chainId) => {
36+
return idToChain.get(chainId);
37+
})
38+
.filter((v) => !!v);
39+
40+
const otherChains = allChains.filter(
41+
(chain) => !priorityChainsSet.has(chain.chainId),
42+
);
43+
44+
sortedChains = [...priorityChains, ...otherChains];
45+
}
46+
47+
return sortedChains.map((chain) => {
2648
return {
2749
label: cleanChainName(chain.name),
2850
value: String(chain.chainId),
2951
};
3052
});
31-
}, [allChains]);
53+
}, [allChains, props.priorityChains, idToChain]);
3254

3355
const searchFn = useCallback(
3456
(option: Option, searchValue: string) => {

apps/dashboard/src/@/components/blocks/Sidebar.tsx

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ import { ExternalLinkIcon } from "lucide-react";
22
import type React from "react";
33
import { cn } from "../../lib/utils";
44
import { NavLink } from "../ui/NavLink";
5+
import { Separator } from "../ui/separator";
56

67
export type SidebarBaseLink = {
78
href: string;
89
label: React.ReactNode;
910
exactMatch?: boolean;
11+
icon?: React.FC<{ className?: string }>;
1012
tracking?: {
1113
category: string;
1214
action: string;
@@ -19,6 +21,9 @@ export type SidebarLink =
1921
| {
2022
group: string;
2123
links: SidebarBaseLink[];
24+
}
25+
| {
26+
separator: true;
2227
};
2328

2429
type SidebarContentProps = {
@@ -27,21 +32,18 @@ type SidebarContentProps = {
2732
className?: string;
2833
};
2934

30-
export function Sidebar(props: SidebarContentProps) {
35+
export function CustomSidebar(props: SidebarContentProps) {
3136
return (
32-
<aside
33-
className={cn(
34-
"sticky top-0 hidden w-[230px] flex-shrink-0 self-start lg:block",
35-
props.className,
36-
)}
37-
>
38-
<div className="py-7">
39-
{props.header}
40-
<div className="flex flex-col gap-1">
41-
<RenderSidebarLinks links={props.links} />
37+
<div className={cn("hidden w-[230px] shrink-0 lg:block", props.className)}>
38+
<aside className="sticky top-0 self-start">
39+
<div className="py-7">
40+
{props.header}
41+
<div className="flex flex-col gap-1">
42+
<RenderSidebarLinks links={props.links} />
43+
</div>
4244
</div>
43-
</div>
44-
</aside>
45+
</aside>
46+
</div>
4547
);
4648
}
4749

@@ -61,16 +63,21 @@ export function RenderSidebarLinks(props: { links: SidebarLink[] }) {
6163
);
6264
}
6365

66+
if ("separator" in link) {
67+
return <Separator className="my-2" />;
68+
}
69+
6470
const isExternal = link.href.startsWith("http");
6571
return (
6672
<NavLink
6773
// biome-ignore lint/suspicious/noArrayIndexKey: items won't be reordered
6874
key={i}
6975
href={link.href}
7076
className="flex items-center gap-2 rounded-md px-3 py-2 text-muted-foreground text-sm hover:bg-accent"
71-
activeClassName="text-foreground"
77+
activeClassName="text-foreground bg-accent"
7278
exactMatch={link.exactMatch}
7379
>
80+
{link.icon && <link.icon className="size-4" />}
7481
{link.label}
7582
{isExternal && <ExternalLinkIcon className="size-3" />}
7683
</NavLink>

apps/dashboard/src/@/components/blocks/SidebarLayout.tsx

Lines changed: 134 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
1+
import {
2+
Sidebar,
3+
SidebarContent,
4+
SidebarFooter,
5+
SidebarGroup,
6+
SidebarGroupContent,
7+
SidebarMenu,
8+
SidebarMenuButton,
9+
SidebarMenuItem,
10+
SidebarRail,
11+
SidebarSeparator,
12+
SidebarTrigger,
13+
useSidebar,
14+
} from "@/components/ui/sidebar";
115
import { cn } from "../../lib/utils";
2-
import { MobileSidebar } from "./MobileSidebar";
3-
import { Sidebar, type SidebarLink } from "./Sidebar";
16+
import { NavLink } from "../ui/NavLink";
17+
import { Separator } from "../ui/separator";
18+
import { MobileSidebar, useActiveSidebarLink } from "./MobileSidebar";
19+
import { CustomSidebar, type SidebarLink } from "./Sidebar";
420

521
export function SidebarLayout(props: {
622
sidebarLinks: SidebarLink[];
@@ -17,7 +33,10 @@ export function SidebarLayout(props: {
1733
props.className,
1834
)}
1935
>
20-
<Sidebar links={sidebarLinks} className={props.desktopSidebarClassName} />
36+
<CustomSidebar
37+
links={sidebarLinks}
38+
className={props.desktopSidebarClassName}
39+
/>
2140
<MobileSidebar
2241
links={sidebarLinks}
2342
triggerClassName={props.mobileSidebarClassName}
@@ -29,3 +48,115 @@ export function SidebarLayout(props: {
2948
</div>
3049
);
3150
}
51+
52+
export function FullWidthSidebarLayout(props: {
53+
contentSidebarLinks: SidebarLink[];
54+
footerSidebarLinks?: SidebarLink[];
55+
children: React.ReactNode;
56+
className?: string;
57+
footer?: React.ReactNode;
58+
}) {
59+
const { contentSidebarLinks, children, footerSidebarLinks } = props;
60+
return (
61+
<div
62+
className={cn("flex w-full flex-1 overflow-y-hidden", props.className)}
63+
>
64+
{/* left - sidebar */}
65+
<Sidebar collapsible="icon" side="left">
66+
<SidebarContent>
67+
<SidebarGroup>
68+
<SidebarGroupContent>
69+
<RenderSidebarGroup
70+
sidebarLinks={contentSidebarLinks}
71+
groupName={undefined}
72+
/>
73+
</SidebarGroupContent>
74+
</SidebarGroup>
75+
</SidebarContent>
76+
77+
{footerSidebarLinks && (
78+
<SidebarFooter className="pb-3">
79+
<RenderSidebarGroup
80+
sidebarLinks={footerSidebarLinks}
81+
groupName={undefined}
82+
/>
83+
</SidebarFooter>
84+
)}
85+
86+
<SidebarRail />
87+
</Sidebar>
88+
89+
{/* right - content */}
90+
<div className="flex h-full flex-grow flex-col overflow-y-auto">
91+
<MobileSidebarTrigger
92+
links={[...contentSidebarLinks, ...(footerSidebarLinks || [])]}
93+
/>
94+
95+
<main className="container z-0 flex min-w-0 max-w-[1280px] grow flex-col pb-20 max-sm:w-full lg:pt-6">
96+
{children}
97+
</main>
98+
{props.footer}
99+
</div>
100+
</div>
101+
);
102+
}
103+
104+
function RenderSidebarGroup(props: {
105+
sidebarLinks: SidebarLink[];
106+
groupName: string | undefined;
107+
}) {
108+
const { sidebarLinks } = props;
109+
const sidebar = useSidebar();
110+
111+
return (
112+
<SidebarMenu className="gap-1.5">
113+
{sidebarLinks.map((link) => {
114+
if ("href" in link) {
115+
return (
116+
<SidebarMenuItem key={link.href}>
117+
<SidebarMenuButton asChild>
118+
<NavLink
119+
href={link.href}
120+
className="flex items-center gap-2 text-muted-foreground text-sm hover:bg-accent"
121+
activeClassName="text-foreground bg-accent"
122+
exactMatch={link.exactMatch}
123+
tracking={link.tracking}
124+
onClick={() => {
125+
sidebar.setOpenMobile(false);
126+
}}
127+
>
128+
{link.icon && <link.icon className="size-4" />}
129+
<span>{link.label}</span>
130+
</NavLink>
131+
</SidebarMenuButton>
132+
</SidebarMenuItem>
133+
);
134+
}
135+
136+
if ("separator" in link) {
137+
return <SidebarSeparator className="my-1" />;
138+
}
139+
140+
return (
141+
<RenderSidebarGroup
142+
sidebarLinks={link.links}
143+
groupName={link.group}
144+
key={link.group}
145+
/>
146+
);
147+
})}
148+
</SidebarMenu>
149+
);
150+
}
151+
152+
function MobileSidebarTrigger(props: { links: SidebarLink[] }) {
153+
const activeLink = useActiveSidebarLink(props.links);
154+
155+
return (
156+
<div className="mb-4 flex items-center gap-3 border-b px-4 py-4 lg:hidden">
157+
<SidebarTrigger className="size-4" />
158+
<Separator orientation="vertical" className="h-4" />
159+
{activeLink && <span className="text-sm">{activeLink.label}</span>}
160+
</div>
161+
);
162+
}

apps/dashboard/src/@/components/ui/NavLink.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export type NavButtonProps = {
1515
action: string;
1616
label: string;
1717
};
18+
onClick?: () => void;
1819
};
1920

2021
export function NavLink(props: React.PropsWithChildren<NavButtonProps>) {
@@ -30,7 +31,9 @@ export function NavLink(props: React.PropsWithChildren<NavButtonProps>) {
3031
href={props.href}
3132
className={cn(props.className, isActive && props.activeClassName)}
3233
target={props.href.startsWith("http") ? "_blank" : undefined}
34+
prefetch={false}
3335
onClick={() => {
36+
props.onClick?.();
3437
if (props.tracking) {
3538
track({
3639
category: props.tracking.category,

0 commit comments

Comments
 (0)