-
-
Notifications
You must be signed in to change notification settings - Fork 10.2k
/
components.tsx
158 lines (144 loc) · 4.74 KB
/
components.tsx
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import * as React from "react";
import type { To } from "history";
import { Action, createPath, parsePath, Location } from "history";
// Get useHistory from react-router-dom v5 (peer dep).
// @ts-expect-error
import { useHistory, Route as RouteV5 } from "react-router-dom";
// We are a wrapper around react-router-dom v6, so bring it in
// and bundle it because an app can't have two versions of
// react-router-dom in its package.json.
import { Router, Routes, Route } from "../react-router-dom";
// v5 isn't in TypeScript, they'll also lose the @types/react-router with this
// but not worried about that for now.
export function CompatRoute(props: any) {
let { location, path } = props;
if (!props.exact) path += "/*";
return (
<Routes location={location}>
<Route path={path} element={<RouteV5 {...props} />} />
</Routes>
);
}
// Copied with 💜 from https://github.com/bvaughn/react-resizable-panels/blob/main/packages/react-resizable-panels/src/hooks/useIsomorphicEffect.ts
const canUseEffectHooks = !!(
typeof window !== "undefined" &&
typeof window.document !== "undefined" &&
typeof window.document.createElement !== "undefined"
);
const useIsomorphicLayoutEffect = canUseEffectHooks
? React.useLayoutEffect
: () => {};
export function CompatRouter({ children }: { children: React.ReactNode }) {
let history = useHistory();
let [state, setState] = React.useState(() => ({
location: history.location,
action: history.action,
}));
useIsomorphicLayoutEffect(() => {
history.listen((location: Location, action: Action) =>
setState({ location, action })
);
}, [history]);
return (
<Router
navigationType={state.action}
location={state.location}
navigator={history}
>
<Routes>
<Route path="*" element={children} />
</Routes>
</Router>
);
}
export interface StaticRouterProps {
basename?: string;
children?: React.ReactNode;
location: Partial<Location> | string;
}
const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
/**
* A `<Router>` that may not navigate to any other location. This is useful
* on the server where there is no stateful UI.
*/
export function StaticRouter({
basename,
children,
location: locationProp = "/",
}: StaticRouterProps) {
if (typeof locationProp === "string") {
locationProp = parsePath(locationProp);
}
let action = Action.Pop;
let location = {
pathname: (locationProp.pathname || "/") as "" | `/${string}`,
search: (locationProp.search || "") as "" | `?${string}`,
hash: (locationProp.hash || "") as "" | `#${string}`,
state: locationProp.state || null,
key: locationProp.key || "default",
} satisfies Location;
let staticNavigator = {
createHref(to: To) {
return typeof to === "string" ? to : createPath(to);
},
encodeLocation(to: To) {
let href = typeof to === "string" ? to : createPath(to);
// Treating this as a full URL will strip any trailing spaces so we need to
// pre-encode them since they might be part of a matching splat param from
// an ancestor route
href = href.replace(/ $/, "%20");
let encoded = ABSOLUTE_URL_REGEX.test(href)
? new URL(href)
: new URL(href, "http://localhost");
return {
pathname: encoded.pathname,
search: encoded.search,
hash: encoded.hash,
};
},
push(to: To) {
throw new Error(
`You cannot use navigator.push() on the server because it is a stateless ` +
`environment. This error was probably triggered when you did a ` +
`\`navigate(${JSON.stringify(to)})\` somewhere in your app.`
);
},
replace(to: To) {
throw new Error(
`You cannot use navigator.replace() on the server because it is a stateless ` +
`environment. This error was probably triggered when you did a ` +
`\`navigate(${JSON.stringify(to)}, { replace: true })\` somewhere ` +
`in your app.`
);
},
go(delta: number) {
throw new Error(
`You cannot use navigator.go() on the server because it is a stateless ` +
`environment. This error was probably triggered when you did a ` +
`\`navigate(${delta})\` somewhere in your app.`
);
},
back() {
throw new Error(
`You cannot use navigator.back() on the server because it is a stateless ` +
`environment.`
);
},
forward() {
throw new Error(
`You cannot use navigator.forward() on the server because it is a stateless ` +
`environment.`
);
},
};
return (
<Router
basename={basename}
children={children}
location={location}
navigationType={action}
navigator={staticNavigator}
static={true}
/>
);
}