forked from StellarSplit/StellarSplit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRootLayout.tsx
More file actions
129 lines (115 loc) · 4.02 KB
/
Copy pathRootLayout.tsx
File metadata and controls
129 lines (115 loc) · 4.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { Component, type ReactNode, useEffect, useState } from "react";
import { Link, Outlet, useLocation } from "react-router-dom";
import Navbar from "../components/Navbar";
import Sidebar from "../components/Sidebar";
type ShellErrorBoundaryProps = {
children: ReactNode;
resetKey: string;
};
type ShellErrorBoundaryState = {
error: Error | null;
};
class ShellErrorBoundary extends Component<
ShellErrorBoundaryProps,
ShellErrorBoundaryState
> {
state: ShellErrorBoundaryState = { error: null };
static getDerivedStateFromError(error: Error): ShellErrorBoundaryState {
return { error };
}
componentDidUpdate(prevProps: ShellErrorBoundaryProps) {
if (prevProps.resetKey !== this.props.resetKey && this.state.error) {
this.setState({ error: null });
}
}
handleRetry = () => this.setState({ error: null });
render() {
if (this.state.error) {
return (
<section className="mx-auto flex min-h-[calc(100vh-3.5rem)] w-full max-w-4xl items-center justify-center p-6 sm:p-8">
<div className="w-full rounded-3xl border border-theme bg-card-theme p-6 shadow-sm sm:p-8">
<p className="text-xs font-semibold uppercase tracking-[0.28em] text-accent">
Page Error
</p>
<h1 className="mt-3 text-2xl font-semibold text-theme">
We could not render this page
</h1>
<p className="mt-3 max-w-2xl text-sm leading-6 text-muted-theme">
{this.state.error.message ||
"Something went wrong while loading the current screen."}
</p>
<div className="mt-6 flex flex-wrap gap-3">
<button
type="button"
onClick={this.handleRetry}
className="rounded-full bg-accent px-4 py-2 text-sm font-semibold text-white"
>
Try Again
</button>
<Link
to="/dashboard"
className="rounded-full border border-theme px-4 py-2 text-sm font-semibold text-theme"
>
Open Dashboard
</Link>
</div>
</div>
</section>
);
}
return this.props.children;
}
}
const STORAGE_KEY = "sidebar-open";
export default function RootLayout() {
const { pathname } = useLocation();
// Desktop: restore saved preference. Mobile: always start closed.
const [isSidebarOpen, setIsSidebarOpen] = useState<boolean>(() => {
if (typeof window === "undefined") return false;
if (window.innerWidth >= 1024) {
return localStorage.getItem(STORAGE_KEY) !== "false";
}
return false;
});
// Persist desktop preference whenever it changes
useEffect(() => {
localStorage.setItem(STORAGE_KEY, String(isSidebarOpen));
}, [isSidebarOpen]);
// On mobile, close when navigating to a new page
useEffect(() => {
if (window.innerWidth < 1024) {
setIsSidebarOpen(false);
}
}, [pathname]);
const toggle = () => setIsSidebarOpen((v) => !v);
return (
<div className="min-h-screen bg-theme text-theme">
<Sidebar
isOpen={isSidebarOpen}
onClose={() => setIsSidebarOpen(false)}
/>
{/*
* On desktop (lg+): shift the content column right when the sidebar
* is open. lg:pl-[14rem] is applied only when isSidebarOpen is true.
* On mobile: sidebar overlays (fixed + backdrop) so no padding shift.
*
* transition-[padding-left] animates the shift to match the sidebar
* slide (280ms cubic-bezier matches Sidebar.tsx transition value).
*/}
<div
className={[
"min-h-screen",
"[transition:padding-left_280ms_cubic-bezier(0.4,0,0.2,1)]",
isSidebarOpen ? "lg:pl-[14rem]" : "lg:pl-0",
].join(" ")}
>
<Navbar onMenuOpen={toggle} />
<main id="main-content" className="min-h-[calc(100vh-3.5rem)]">
<ShellErrorBoundary resetKey={pathname}>
<Outlet />
</ShellErrorBoundary>
</main>
</div>
</div>
);
}