clear-react-router 1.4.6 → 1.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/components/Router.d.ts +2 -1
- package/dist/context/RouterProviderContext.d.ts +5 -11
- package/dist/hooks/useHandleNavigation.d.ts +10 -5
- package/dist/hooks/useLoader.d.ts +7 -5
- package/dist/hooks/usePreserveScroll.d.ts +1 -7
- package/dist/hooks/useServiceContext.d.ts +0 -1
- package/dist/index.js +165 -194
- package/dist/provider/Provider.d.ts +3 -3
- package/dist/types/global.d.ts +8 -8
- package/package.json +1 -1
- package/dist/context/RouterViewContext.d.ts +0 -1
- package/dist/provider/ViewProvider.d.ts +0 -7
package/README.md
CHANGED
|
@@ -45,7 +45,6 @@ The root component that provides routing context to the application. Place stati
|
|
|
45
45
|
|------|------|---------|-------------|
|
|
46
46
|
| `routeList` | `RouteItem[]` | required | Array of route configurations |
|
|
47
47
|
| `context` | `object` | `{}` | Initial context (user, theme, etc.) |
|
|
48
|
-
| `preserveScroll` | `boolean` | `true` | Save and restore scroll position when navigating between pages |
|
|
49
48
|
| `children` | `ReactNode` | required | App content (must include `<Router />`) |
|
|
50
49
|
|
|
51
50
|
```
|
|
@@ -71,6 +70,8 @@ Renders the current route's component. Must be placed inside `<RouterProvider>`.
|
|
|
71
70
|
| `isAnimated` | `boolean \| undefined` | `false` | Enable smooth page fade transitions |
|
|
72
71
|
| `animationDuration` | `number` | `optional` | Animation duration in milliseconds (browser default is used if not set) |
|
|
73
72
|
| `spinner` | `boolean \| undefined` | `true` | Show a small spinner in the corner while loading data (only when `isAnimated` is enabled) |
|
|
73
|
+
| `preserveScroll` | `boolean \| undefined` | `true` | Save and restore scroll position when navigating between pages |
|
|
74
|
+
| `showFallbackIfAnimated` | `boolean \| undefined` | `false` | Show `loaderFallback` even when `isAnimated` is `true` (instead of spinner) |
|
|
74
75
|
|
|
75
76
|
```
|
|
76
77
|
<RouterProvider routeList={routes} isAnimated>
|
|
@@ -3,6 +3,7 @@ type RouterProps = {
|
|
|
3
3
|
animationDuration?: number;
|
|
4
4
|
spinner?: boolean;
|
|
5
5
|
preserveScroll?: boolean;
|
|
6
|
+
showFallbackIfAnimated?: boolean;
|
|
6
7
|
};
|
|
7
|
-
export declare const Router: ({ isAnimated, animationDuration, spinner, preserveScroll }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
+
export declare const Router: ({ isAnimated, animationDuration, spinner, preserveScroll, showFallbackIfAnimated, }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
8
9
|
export {};
|
|
@@ -1,26 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BlockerState, LoaderState, Location, NextItemData, RouteItem, UpdateBlockedRouteProps } from '../types/global';
|
|
3
|
-
export type PropsContextValue = {
|
|
4
|
-
routeList: RouteItem[];
|
|
5
|
-
};
|
|
1
|
+
import { BlockerState, Location, RouteItem, RouteItemData, UpdateBlockedRouteProps } from '../types/global';
|
|
6
2
|
export type NavigationContextValue = {
|
|
7
|
-
location: Location;
|
|
8
3
|
blockerState: BlockerState;
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
routeItemData: RouteItemData;
|
|
5
|
+
currentLoaderFallback: RouteItem['loaderFallback'];
|
|
11
6
|
};
|
|
12
7
|
export type ActionsContextValue = {
|
|
13
|
-
|
|
8
|
+
setSearch(arg: string): void;
|
|
14
9
|
updateLocation(route: Location): Promise<void>;
|
|
15
10
|
updateBlockedRoute(arg: UpdateBlockedRouteProps): void;
|
|
16
11
|
prefetchLoader(arg: string): Promise<void>;
|
|
17
12
|
setContext(arg: object): void;
|
|
13
|
+
restoreScroll(): void;
|
|
18
14
|
};
|
|
19
15
|
export type DataContextValue = {
|
|
20
|
-
loaderState: LoaderState;
|
|
21
16
|
context: Record<string, unknown>;
|
|
22
17
|
};
|
|
23
|
-
export declare const PropsContext: import("react").Context<PropsContextValue>;
|
|
24
18
|
export declare const ActionsContext: import("react").Context<ActionsContextValue>;
|
|
25
19
|
export declare const DataContext: import("react").Context<DataContextValue>;
|
|
26
20
|
export declare const NavigationContext: import("react").Context<NavigationContextValue>;
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { type Dispatch, type SetStateAction } from 'react';
|
|
2
|
-
import { BlockerState,
|
|
2
|
+
import { BlockerState, Location, RevalidateCacheArgs, RouteItem, RouteItemData, UpdateBlockedRouteProps } from '../types/global';
|
|
3
3
|
type UseHandleNavigation = {
|
|
4
4
|
routeList: RouteItem[];
|
|
5
|
-
setLocation: (arg: Location) => void;
|
|
6
5
|
context: Record<string, unknown>;
|
|
7
6
|
revalidateCache(arg: RevalidateCacheArgs): Promise<void>;
|
|
8
7
|
setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
|
|
9
|
-
|
|
8
|
+
isCacheItemFresh(arg: {
|
|
9
|
+
routeItem?: RouteItem;
|
|
10
|
+
pathname: string;
|
|
11
|
+
}): boolean;
|
|
10
12
|
};
|
|
11
|
-
export declare const useHandleNavigation: ({
|
|
13
|
+
export declare const useHandleNavigation: ({ routeList, context, revalidateCache, setContext, isCacheItemFresh, }: UseHandleNavigation) => {
|
|
12
14
|
blockerState: BlockerState;
|
|
13
15
|
updateLocation: (nextLocation: Location) => Promise<void>;
|
|
14
16
|
updateBlockedRoute: ({ type, payload }: UpdateBlockedRouteProps) => void;
|
|
15
|
-
|
|
17
|
+
routeItemData: RouteItemData;
|
|
18
|
+
setSearch: (search: string) => void;
|
|
19
|
+
restoreScroll: () => void;
|
|
20
|
+
currentLoaderFallback: import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | (() => import("react").ReactElement) | undefined;
|
|
16
21
|
};
|
|
17
22
|
export {};
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { type Dispatch, type SetStateAction } from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { RevalidateCacheArgs, RouteItem } from '../types/global';
|
|
3
3
|
type UseLoaderParams = {
|
|
4
4
|
routeList: RouteItem[];
|
|
5
5
|
context: Record<string, unknown>;
|
|
6
6
|
setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
|
|
7
|
-
setLoaderState: Dispatch<SetStateAction<LoaderState>>;
|
|
8
7
|
};
|
|
9
|
-
export declare const useLoader: ({ routeList, context, setContext
|
|
8
|
+
export declare const useLoader: ({ routeList, context, setContext }: UseLoaderParams) => {
|
|
10
9
|
prefetchLoader: (pathname: string) => Promise<void>;
|
|
11
|
-
revalidateCache: ({ routeItem,
|
|
12
|
-
|
|
10
|
+
revalidateCache: ({ routeItem, loaderState, pathname }: RevalidateCacheArgs) => Promise<void>;
|
|
11
|
+
isCacheItemFresh: ({ routeItem, pathname }: {
|
|
12
|
+
routeItem?: RouteItem;
|
|
13
|
+
pathname: string;
|
|
14
|
+
}) => boolean;
|
|
13
15
|
};
|
|
14
16
|
export {};
|
|
@@ -1,7 +1 @@
|
|
|
1
|
-
|
|
2
|
-
pathname: string;
|
|
3
|
-
preserveScroll: boolean;
|
|
4
|
-
nextPathname?: string;
|
|
5
|
-
};
|
|
6
|
-
export declare const usePreserveScroll: ({ pathname, preserveScroll, nextPathname }: UsePreserveScrollParams) => void;
|
|
7
|
-
export {};
|
|
1
|
+
export declare const usePreserveScroll: (preserveScroll: boolean) => void;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
export declare const useNavigationState: () => import("../context/RouterProviderContext").NavigationContextValue;
|
|
2
2
|
export declare const useRouterActions: () => import("../context/RouterProviderContext").ActionsContextValue;
|
|
3
3
|
export declare const useRouterData: () => import("../context/RouterProviderContext").DataContextValue;
|
|
4
|
-
export declare const usePropsData: () => import("../context/RouterProviderContext").PropsContextValue;
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,6 @@ import { Suspense, createContext, lazy, useCallback, useContext, useEffect, useM
|
|
|
3
3
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
4
4
|
//#endregion
|
|
5
5
|
//#region context/RouterProviderContext.ts
|
|
6
|
-
var PropsContext = createContext({});
|
|
7
6
|
var ActionsContext = createContext({});
|
|
8
7
|
var DataContext = createContext({});
|
|
9
8
|
var NavigationContext = createContext({});
|
|
@@ -19,7 +18,7 @@ var NavigationContext = createContext({});
|
|
|
19
18
|
* LICENSE file in the root directory of this source tree.
|
|
20
19
|
*/
|
|
21
20
|
var require_react_jsx_runtime_production = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
22
|
-
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element");
|
|
21
|
+
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
|
|
23
22
|
function jsxProd(type, config, maybeKey) {
|
|
24
23
|
var key = null;
|
|
25
24
|
void 0 !== maybeKey && (key = "" + maybeKey);
|
|
@@ -37,6 +36,7 @@ var require_react_jsx_runtime_production = /* @__PURE__ */ __commonJSMin(((expor
|
|
|
37
36
|
props: maybeKey
|
|
38
37
|
};
|
|
39
38
|
}
|
|
39
|
+
exports.Fragment = REACT_FRAGMENT_TYPE;
|
|
40
40
|
exports.jsx = jsxProd;
|
|
41
41
|
exports.jsxs = jsxProd;
|
|
42
42
|
}));
|
|
@@ -45,31 +45,25 @@ var require_react_jsx_runtime_production = /* @__PURE__ */ __commonJSMin(((expor
|
|
|
45
45
|
var import_jsx_runtime = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
46
46
|
module.exports = require_react_jsx_runtime_production();
|
|
47
47
|
})))();
|
|
48
|
-
var Provider = ({ children, setContext, context, updateBlockedRoute, updateLocation,
|
|
49
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
50
|
-
value: {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
48
|
+
var Provider = ({ children, setContext, context, updateBlockedRoute, updateLocation, setSearch, prefetchLoader, blockerState, routeItemData, restoreScroll, currentLoaderFallback }) => {
|
|
49
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActionsContext.Provider, {
|
|
50
|
+
value: {
|
|
51
|
+
setSearch,
|
|
52
|
+
updateLocation,
|
|
53
|
+
updateBlockedRoute,
|
|
54
|
+
prefetchLoader,
|
|
55
|
+
setContext,
|
|
56
|
+
restoreScroll
|
|
57
|
+
},
|
|
58
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DataContext.Provider, {
|
|
59
|
+
value: { context },
|
|
60
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavigationContext.Provider, {
|
|
60
61
|
value: {
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
blockerState,
|
|
63
|
+
routeItemData,
|
|
64
|
+
currentLoaderFallback
|
|
63
65
|
},
|
|
64
|
-
children
|
|
65
|
-
value: {
|
|
66
|
-
blockerState,
|
|
67
|
-
location,
|
|
68
|
-
isLoading,
|
|
69
|
-
nextItemData
|
|
70
|
-
},
|
|
71
|
-
children
|
|
72
|
-
})
|
|
66
|
+
children
|
|
73
67
|
})
|
|
74
68
|
})
|
|
75
69
|
});
|
|
@@ -136,75 +130,106 @@ var comparePaths = (el, pathname) => {
|
|
|
136
130
|
};
|
|
137
131
|
//#endregion
|
|
138
132
|
//#region hooks/useHandleNavigation.ts
|
|
139
|
-
var
|
|
133
|
+
var ALL_LOCATIONS = "*";
|
|
134
|
+
var useHandleNavigation = ({ routeList, context, revalidateCache, setContext, isCacheItemFresh }) => {
|
|
140
135
|
const [blockedRoute, setBlockedRoute] = useState({
|
|
141
136
|
from: "",
|
|
142
137
|
to: ""
|
|
143
138
|
});
|
|
144
|
-
const [
|
|
139
|
+
const [routeItemData, setRouteItemData] = useState({
|
|
140
|
+
location: {},
|
|
141
|
+
routeItem: void 0,
|
|
142
|
+
loaderState: {}
|
|
143
|
+
});
|
|
144
|
+
const [scrollMap, setScrollMap] = useState({});
|
|
145
|
+
const [currentLoaderFallback, setCurrentLoaderFallback] = useState();
|
|
146
|
+
const loaderState = useRef({});
|
|
145
147
|
const prevPathname = useRef("");
|
|
146
148
|
const navigationSeq = useRef(0);
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
+
const scrollMapRef = useLatest(scrollMap);
|
|
150
|
+
const setSearch = useCallback((search) => setRouteItemData((prevState) => ({
|
|
151
|
+
...prevState,
|
|
152
|
+
location: {
|
|
153
|
+
...prevState.location,
|
|
154
|
+
search
|
|
155
|
+
}
|
|
156
|
+
})), []);
|
|
157
|
+
const restoreScroll = useCallback(() => {
|
|
158
|
+
if (!prevPathname.current || !scrollMapRef.current[prevPathname.current]) return;
|
|
159
|
+
requestAnimationFrame(() => {
|
|
160
|
+
window.scrollTo({
|
|
161
|
+
top: scrollMapRef.current[prevPathname.current],
|
|
162
|
+
behavior: "smooth"
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
}, [scrollMapRef]);
|
|
166
|
+
const navigation = useCallback((nextLocation, routeItem) => {
|
|
167
|
+
setRouteItemData({
|
|
168
|
+
location: nextLocation,
|
|
169
|
+
routeItem,
|
|
170
|
+
loaderState: loaderState.current
|
|
171
|
+
});
|
|
172
|
+
setCurrentLoaderFallback(void 0);
|
|
149
173
|
prevPathname.current = nextLocation.pathname;
|
|
150
174
|
const fullPath = nextLocation.search ? `${nextLocation.pathname}${nextLocation.search}` : nextLocation.pathname;
|
|
151
175
|
if (fullPath === window.location.pathname + window.location.search) return;
|
|
152
176
|
history.pushState(null, "", fullPath);
|
|
153
|
-
}, [
|
|
154
|
-
const transitionedNavigation = useCallback((nextLocation) => {
|
|
177
|
+
}, []);
|
|
178
|
+
const transitionedNavigation = useCallback((nextLocation, routeItem) => {
|
|
179
|
+
setScrollMap((prevState) => {
|
|
180
|
+
const scrollPosition = document.scrollingElement?.scrollTop ?? 0;
|
|
181
|
+
if (!scrollPosition || prevState[prevPathname.current] === scrollPosition) return prevState;
|
|
182
|
+
return {
|
|
183
|
+
...prevState,
|
|
184
|
+
[prevPathname.current]: scrollPosition
|
|
185
|
+
};
|
|
186
|
+
});
|
|
155
187
|
try {
|
|
156
|
-
document.startViewTransition(() => navigation(nextLocation));
|
|
188
|
+
document.startViewTransition(() => navigation(nextLocation, routeItem));
|
|
157
189
|
} catch {
|
|
158
|
-
navigation(nextLocation);
|
|
190
|
+
navigation(nextLocation, routeItem);
|
|
159
191
|
}
|
|
160
192
|
}, [navigation]);
|
|
161
193
|
const navigationHandler = useCallback(async (nextLocation) => {
|
|
162
194
|
navigationSeq.current = navigationSeq.current + 1;
|
|
163
195
|
const seq = navigationSeq.current;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
loaderFallback: nextItem?.loaderFallback,
|
|
167
|
-
params: nextItem?.params,
|
|
168
|
-
pathname: nextLocation.pathname
|
|
169
|
-
});
|
|
196
|
+
loaderState.current = {};
|
|
197
|
+
const nextItem = routeList.find((el) => el.path === ALL_LOCATIONS || comparePaths(el, nextLocation.pathname));
|
|
170
198
|
const params = getParamsObject({
|
|
171
199
|
params: nextItem?.params,
|
|
172
200
|
pathname: nextLocation.pathname
|
|
173
201
|
});
|
|
174
202
|
if (nextItem?.beforeLoad) try {
|
|
175
|
-
const redirect = async (location) => typeof location === "string" ?
|
|
203
|
+
const redirect = async (location) => await navigationHandler(typeof location === "string" ? { pathname: location } : location);
|
|
176
204
|
await nextItem.beforeLoad({
|
|
177
205
|
context,
|
|
178
206
|
redirect,
|
|
179
207
|
params,
|
|
180
208
|
setContext
|
|
181
209
|
});
|
|
182
|
-
|
|
183
|
-
...
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
beforeLoadError: null
|
|
187
|
-
}
|
|
188
|
-
}));
|
|
210
|
+
loaderState.current = {
|
|
211
|
+
...loaderState.current,
|
|
212
|
+
beforeLoadError: null
|
|
213
|
+
};
|
|
189
214
|
} catch (error) {
|
|
190
|
-
|
|
191
|
-
...
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
}));
|
|
197
|
-
transitionedNavigation(nextLocation);
|
|
198
|
-
return;
|
|
215
|
+
loaderState.current = {
|
|
216
|
+
...loaderState.current,
|
|
217
|
+
beforeLoadError: error
|
|
218
|
+
};
|
|
219
|
+
return transitionedNavigation(nextLocation, nextItem);
|
|
199
220
|
}
|
|
200
221
|
if (seq !== navigationSeq.current) return;
|
|
222
|
+
setCurrentLoaderFallback(isCacheItemFresh({
|
|
223
|
+
routeItem: nextItem,
|
|
224
|
+
pathname: nextLocation.pathname
|
|
225
|
+
}) ? void 0 : nextItem?.loaderFallback);
|
|
201
226
|
await revalidateCache({
|
|
202
227
|
routeItem: nextItem,
|
|
203
|
-
|
|
228
|
+
loaderState,
|
|
204
229
|
pathname: nextLocation.pathname
|
|
205
230
|
});
|
|
206
231
|
if (seq !== navigationSeq.current) return;
|
|
207
|
-
transitionedNavigation(nextLocation);
|
|
232
|
+
transitionedNavigation(nextLocation, nextItem);
|
|
208
233
|
if (nextItem?.afterLoad) await nextItem.afterLoad({
|
|
209
234
|
context,
|
|
210
235
|
params,
|
|
@@ -216,7 +241,7 @@ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, s
|
|
|
216
241
|
routeList,
|
|
217
242
|
transitionedNavigation,
|
|
218
243
|
setContext,
|
|
219
|
-
|
|
244
|
+
isCacheItemFresh
|
|
220
245
|
]);
|
|
221
246
|
const setNextLocationRef = useLatest(navigationHandler);
|
|
222
247
|
const updateBlockedRoute = useCallback(({ type, payload = "" }) => setBlockedRoute((prevState) => {
|
|
@@ -270,14 +295,17 @@ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, s
|
|
|
270
295
|
}, [blockedRoute]),
|
|
271
296
|
updateLocation,
|
|
272
297
|
updateBlockedRoute,
|
|
273
|
-
|
|
298
|
+
routeItemData,
|
|
299
|
+
setSearch,
|
|
300
|
+
restoreScroll,
|
|
301
|
+
currentLoaderFallback
|
|
274
302
|
};
|
|
275
303
|
};
|
|
276
304
|
//#endregion
|
|
277
305
|
//#region hooks/useLoader.ts
|
|
278
|
-
var useLoader = ({ routeList, context, setContext
|
|
279
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
306
|
+
var useLoader = ({ routeList, context, setContext }) => {
|
|
280
307
|
const cacheTimestampsRef = useRef({});
|
|
308
|
+
const loaderCacheRef = useRef({});
|
|
281
309
|
const isCacheItemFresh = useCallback(({ routeItem, pathname }) => {
|
|
282
310
|
if (!routeItem) return true;
|
|
283
311
|
const currentCacheTimestamp = cacheTimestampsRef.current[pathname];
|
|
@@ -285,13 +313,19 @@ var useLoader = ({ routeList, context, setContext, setLoaderState }) => {
|
|
|
285
313
|
if (!routeItem.staleTime) return true;
|
|
286
314
|
return Date.now() - currentCacheTimestamp < routeItem.staleTime;
|
|
287
315
|
}, []);
|
|
288
|
-
const revalidateCache = useCallback(async ({ routeItem,
|
|
289
|
-
if (!routeItem?.loader)
|
|
316
|
+
const revalidateCache = useCallback(async ({ routeItem, loaderState, pathname }) => {
|
|
317
|
+
if (!routeItem?.loader) {
|
|
318
|
+
if (!loaderState) return;
|
|
319
|
+
if (!routeItem?.beforeLoad) loaderState.current = {};
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
290
322
|
if (isCacheItemFresh({
|
|
291
323
|
routeItem,
|
|
292
324
|
pathname
|
|
293
|
-
}))
|
|
294
|
-
|
|
325
|
+
})) {
|
|
326
|
+
if (loaderState) loaderState.current = loaderCacheRef.current[pathname];
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
295
329
|
try {
|
|
296
330
|
const params = getParamsObject({
|
|
297
331
|
params: routeItem.params,
|
|
@@ -306,31 +340,27 @@ var useLoader = ({ routeList, context, setContext, setLoaderState }) => {
|
|
|
306
340
|
...cacheTimestampsRef.current,
|
|
307
341
|
[pathname]: Date.now()
|
|
308
342
|
};
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
343
|
+
loaderCacheRef.current[pathname] = {
|
|
344
|
+
data: result,
|
|
345
|
+
loaderError: null,
|
|
346
|
+
beforeLoadError: null
|
|
347
|
+
};
|
|
348
|
+
if (loaderState) loaderState.current = {
|
|
349
|
+
...loaderState?.current,
|
|
350
|
+
data: result,
|
|
351
|
+
loaderError: null
|
|
352
|
+
};
|
|
317
353
|
} catch (error) {
|
|
318
|
-
if (
|
|
319
|
-
...
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
loaderError: error
|
|
324
|
-
}
|
|
325
|
-
}));
|
|
326
|
-
} finally {
|
|
327
|
-
setTimeout(() => setIsLoading(false), 10);
|
|
354
|
+
if (loaderState) loaderState.current = {
|
|
355
|
+
...loaderState?.current,
|
|
356
|
+
data: null,
|
|
357
|
+
loaderError: error
|
|
358
|
+
};
|
|
328
359
|
}
|
|
329
360
|
}, [
|
|
330
361
|
context,
|
|
331
362
|
isCacheItemFresh,
|
|
332
|
-
setContext
|
|
333
|
-
setLoaderState
|
|
363
|
+
setContext
|
|
334
364
|
]);
|
|
335
365
|
return {
|
|
336
366
|
prefetchLoader: useCallback(async (pathname) => {
|
|
@@ -341,54 +371,49 @@ var useLoader = ({ routeList, context, setContext, setLoaderState }) => {
|
|
|
341
371
|
});
|
|
342
372
|
}, [revalidateCache, routeList]),
|
|
343
373
|
revalidateCache,
|
|
344
|
-
|
|
374
|
+
isCacheItemFresh
|
|
345
375
|
};
|
|
346
376
|
};
|
|
347
377
|
//#endregion
|
|
348
378
|
//#region components/RouterProvider.tsx
|
|
349
379
|
var RouterProvider = ({ children, routeList, context: initialContext = {} }) => {
|
|
350
|
-
const [location, setLocation] = useState({});
|
|
351
380
|
const [context, setContext] = useState(initialContext);
|
|
352
|
-
const
|
|
353
|
-
const { prefetchLoader, revalidateCache, isLoading } = useLoader({
|
|
381
|
+
const { prefetchLoader, revalidateCache, isCacheItemFresh } = useLoader({
|
|
354
382
|
routeList,
|
|
355
383
|
context,
|
|
356
|
-
setContext
|
|
357
|
-
setLoaderState
|
|
384
|
+
setContext
|
|
358
385
|
});
|
|
359
|
-
const { blockerState, updateLocation, updateBlockedRoute,
|
|
360
|
-
setLocation,
|
|
386
|
+
const { blockerState, updateLocation, updateBlockedRoute, routeItemData, setSearch, restoreScroll, currentLoaderFallback } = useHandleNavigation({
|
|
361
387
|
routeList,
|
|
362
388
|
context,
|
|
363
389
|
setContext,
|
|
364
390
|
revalidateCache,
|
|
365
|
-
|
|
391
|
+
isCacheItemFresh
|
|
366
392
|
});
|
|
367
393
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Provider, {
|
|
368
394
|
...useMemo(() => ({
|
|
369
|
-
|
|
370
|
-
setLocation,
|
|
395
|
+
setSearch,
|
|
371
396
|
updateLocation,
|
|
372
|
-
loaderState,
|
|
373
397
|
prefetchLoader,
|
|
374
398
|
updateBlockedRoute,
|
|
375
399
|
blockerState,
|
|
376
400
|
context,
|
|
377
401
|
setContext,
|
|
378
402
|
routeList,
|
|
379
|
-
|
|
380
|
-
|
|
403
|
+
routeItemData,
|
|
404
|
+
restoreScroll,
|
|
405
|
+
currentLoaderFallback
|
|
381
406
|
}), [
|
|
382
407
|
blockerState,
|
|
383
408
|
context,
|
|
384
|
-
isLoading,
|
|
385
|
-
loaderState,
|
|
386
|
-
location,
|
|
387
409
|
prefetchLoader,
|
|
410
|
+
routeItemData,
|
|
388
411
|
routeList,
|
|
412
|
+
setSearch,
|
|
389
413
|
updateBlockedRoute,
|
|
390
414
|
updateLocation,
|
|
391
|
-
|
|
415
|
+
currentLoaderFallback,
|
|
416
|
+
restoreScroll
|
|
392
417
|
]),
|
|
393
418
|
children
|
|
394
419
|
});
|
|
@@ -403,7 +428,6 @@ var useServiceState = (reactContext) => {
|
|
|
403
428
|
var useNavigationState = () => useServiceState(NavigationContext);
|
|
404
429
|
var useRouterActions = () => useServiceState(ActionsContext);
|
|
405
430
|
var useRouterData = () => useServiceState(DataContext);
|
|
406
|
-
var usePropsData = () => useServiceState(PropsContext);
|
|
407
431
|
//#endregion
|
|
408
432
|
//#region hooks/useApplyCustomAnimation.ts
|
|
409
433
|
var useApplyCustomAnimation = (animationDuration) => {
|
|
@@ -443,46 +467,18 @@ var useApplyCustomAnimation = (animationDuration) => {
|
|
|
443
467
|
};
|
|
444
468
|
//#endregion
|
|
445
469
|
//#region hooks/usePreserveScroll.ts
|
|
446
|
-
var usePreserveScroll = (
|
|
447
|
-
const
|
|
448
|
-
const
|
|
470
|
+
var usePreserveScroll = (preserveScroll) => {
|
|
471
|
+
const { restoreScroll } = useRouterActions();
|
|
472
|
+
const { routeItemData: { location: { pathname } } } = useNavigationState();
|
|
449
473
|
useEffect(() => {
|
|
450
|
-
|
|
451
|
-
const scrollPosition = document.scrollingElement?.scrollTop ?? 0;
|
|
452
|
-
if (!scrollPosition || prevState[prevPathname.current] === scrollPosition) return prevState;
|
|
453
|
-
return {
|
|
454
|
-
...prevState,
|
|
455
|
-
[prevPathname.current]: scrollPosition
|
|
456
|
-
};
|
|
457
|
-
});
|
|
458
|
-
prevPathname.current = pathname;
|
|
459
|
-
}, [pathname, nextPathname]);
|
|
460
|
-
useEffect(() => {
|
|
461
|
-
if (!preserveScroll || !pathname || !scrollMap[pathname]) return;
|
|
462
|
-
requestAnimationFrame(() => {
|
|
463
|
-
window.scrollTo({
|
|
464
|
-
top: scrollMap[pathname],
|
|
465
|
-
behavior: "smooth"
|
|
466
|
-
});
|
|
467
|
-
});
|
|
474
|
+
if (preserveScroll) restoreScroll();
|
|
468
475
|
}, [
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
476
|
+
preserveScroll,
|
|
477
|
+
restoreScroll,
|
|
478
|
+
pathname
|
|
472
479
|
]);
|
|
473
480
|
};
|
|
474
481
|
//#endregion
|
|
475
|
-
//#region context/RouterViewContext.ts
|
|
476
|
-
var RouterViewContext = createContext({});
|
|
477
|
-
//#endregion
|
|
478
|
-
//#region provider/ViewProvider.tsx
|
|
479
|
-
var ViewProvider = ({ children, params }) => {
|
|
480
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterViewContext.Provider, {
|
|
481
|
-
value: params,
|
|
482
|
-
children
|
|
483
|
-
});
|
|
484
|
-
};
|
|
485
|
-
//#endregion
|
|
486
482
|
//#region components/Spinner.tsx
|
|
487
483
|
var Spinner = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cr-spinner" });
|
|
488
484
|
//#endregion
|
|
@@ -493,55 +489,28 @@ var renderElement = (Component) => {
|
|
|
493
489
|
};
|
|
494
490
|
//#endregion
|
|
495
491
|
//#region components/Router.tsx
|
|
496
|
-
var
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
const { routeList } = usePropsData();
|
|
500
|
-
const { loaderState } = useRouterData();
|
|
501
|
-
usePreserveScroll({
|
|
502
|
-
nextPathname: nextItemData.pathname,
|
|
503
|
-
pathname: location.pathname,
|
|
504
|
-
preserveScroll
|
|
505
|
-
});
|
|
492
|
+
var Router = ({ isAnimated, animationDuration, spinner = true, preserveScroll = true, showFallbackIfAnimated = false }) => {
|
|
493
|
+
const { routeItemData: { routeItem, loaderState }, currentLoaderFallback } = useNavigationState();
|
|
494
|
+
usePreserveScroll(preserveScroll);
|
|
506
495
|
useApplyCustomAnimation(animationDuration);
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
nextItemData.params,
|
|
517
|
-
nextItemData.pathname
|
|
518
|
-
]);
|
|
519
|
-
const shouldErrorElementShown = useMemo(() => Boolean(loaderState[location.pathname]?.loaderError || loaderState[location.pathname]?.beforeLoadError), [loaderState, location.pathname]);
|
|
520
|
-
if (!routeItem && !nextItemData.loaderFallback) return null;
|
|
521
|
-
if (!routeItem && nextItemData.loaderFallback) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ViewProvider, {
|
|
522
|
-
params,
|
|
523
|
-
children: renderElement(nextItemData.loaderFallback)
|
|
524
|
-
});
|
|
525
|
-
if (!isAnimated && !shouldErrorElementShown && isLoading && nextItemData.loaderFallback) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ViewProvider, {
|
|
526
|
-
params,
|
|
527
|
-
children: renderElement(nextItemData.loaderFallback)
|
|
528
|
-
});
|
|
529
|
-
if (shouldErrorElementShown) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ViewProvider, {
|
|
530
|
-
params,
|
|
531
|
-
children: [renderElement(routeItem?.errorElement), spinner && isAnimated && isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})]
|
|
532
|
-
});
|
|
533
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
496
|
+
const isLoading = Boolean(currentLoaderFallback);
|
|
497
|
+
const showErrorElement = !isLoading && Boolean(loaderState.loaderError || loaderState.beforeLoadError);
|
|
498
|
+
const showSpinner = spinner && isAnimated && isLoading;
|
|
499
|
+
const loadingContent = !showErrorElement && isLoading;
|
|
500
|
+
if ((showFallbackIfAnimated || !isAnimated) && loadingContent) return renderElement(currentLoaderFallback);
|
|
501
|
+
if (!showFallbackIfAnimated && isAnimated && loadingContent) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {});
|
|
502
|
+
if (!routeItem) return null;
|
|
503
|
+
if (showErrorElement) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [renderElement(routeItem.errorElement), showSpinner && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})] });
|
|
504
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
534
505
|
style: { viewTransitionName: "page" },
|
|
535
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.
|
|
536
|
-
params,
|
|
537
|
-
children: [renderElement(routeItem?.element) || null, spinner && isAnimated && isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})]
|
|
538
|
-
})
|
|
506
|
+
children: [renderElement(routeItem.element) || null, showSpinner && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})]
|
|
539
507
|
});
|
|
540
508
|
};
|
|
541
509
|
//#endregion
|
|
542
510
|
//#region hooks/useLocation.ts
|
|
543
511
|
var useLocation = () => {
|
|
544
|
-
|
|
512
|
+
const { routeItemData: { location } } = useNavigationState();
|
|
513
|
+
return location;
|
|
545
514
|
};
|
|
546
515
|
//#endregion
|
|
547
516
|
//#region hooks/useNavigate.ts
|
|
@@ -588,19 +557,24 @@ var Link = ({ children, to, prefetch = true, prefetchDelay = STANDARD_DELAY }) =
|
|
|
588
557
|
//#endregion
|
|
589
558
|
//#region hooks/useParams.ts
|
|
590
559
|
var useParams = () => {
|
|
591
|
-
|
|
560
|
+
const { routeItemData: { routeItem } } = useNavigationState();
|
|
561
|
+
if (!routeItem) return void 0;
|
|
562
|
+
return getParamsObject({
|
|
563
|
+
params: routeItem?.params,
|
|
564
|
+
pathname: location.pathname
|
|
565
|
+
});
|
|
592
566
|
};
|
|
593
567
|
//#endregion
|
|
594
568
|
//#region hooks/useLoaderState.ts
|
|
595
569
|
var useLoaderState = () => {
|
|
596
|
-
const {
|
|
597
|
-
|
|
598
|
-
return loaderState[pathname];
|
|
570
|
+
const { routeItemData: { loaderState } } = useNavigationState();
|
|
571
|
+
return loaderState;
|
|
599
572
|
};
|
|
600
573
|
//#endregion
|
|
601
574
|
//#region hooks/useBlocker.ts
|
|
602
575
|
var useBlocker = (blockerFn) => {
|
|
603
|
-
const {
|
|
576
|
+
const { blockerState } = useNavigationState();
|
|
577
|
+
const { routeItemData: { location: { pathname } } } = useNavigationState();
|
|
604
578
|
const { updateBlockedRoute } = useRouterActions();
|
|
605
579
|
const shouldBlock = blockerFn();
|
|
606
580
|
useEffect(() => updateBlockedRoute(shouldBlock ? {
|
|
@@ -643,8 +617,8 @@ var useRouterContext = () => {
|
|
|
643
617
|
//#endregion
|
|
644
618
|
//#region hooks/useSearchParams.ts
|
|
645
619
|
var useSearchParams = () => {
|
|
646
|
-
const location =
|
|
647
|
-
const {
|
|
620
|
+
const { routeItemData: { location } } = useNavigationState();
|
|
621
|
+
const { setSearch } = useRouterActions();
|
|
648
622
|
const locationRef = useLatest(location);
|
|
649
623
|
const searchString = useMemo(() => location.search ? location.search.replace("?", "") : location.pathname.split("?")?.[1] ?? "", [location.pathname, location.search]);
|
|
650
624
|
const searchParams = useMemo(() => new URLSearchParams(searchString), [searchString]);
|
|
@@ -656,12 +630,9 @@ var useSearchParams = () => {
|
|
|
656
630
|
const newSearch = params.toString();
|
|
657
631
|
const { pathname } = locationRef.current;
|
|
658
632
|
const search = newSearch ? `?${newSearch}` : "";
|
|
659
|
-
|
|
660
|
-
...prevState,
|
|
661
|
-
search
|
|
662
|
-
}));
|
|
633
|
+
setSearch(search);
|
|
663
634
|
history.replaceState(null, "", pathname + search);
|
|
664
|
-
}, [locationRef,
|
|
635
|
+
}, [locationRef, setSearch]);
|
|
665
636
|
return {
|
|
666
637
|
searchParams,
|
|
667
638
|
getSearchParams,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
|
-
import { type ActionsContextValue, type DataContextValue, type NavigationContextValue
|
|
3
|
-
type ProviderProps =
|
|
2
|
+
import { type ActionsContextValue, type DataContextValue, type NavigationContextValue } from '../context/RouterProviderContext';
|
|
3
|
+
type ProviderProps = NavigationContextValue & ActionsContextValue & DataContextValue & {
|
|
4
4
|
children: ReactNode;
|
|
5
5
|
};
|
|
6
|
-
export declare const Provider: ({ children, setContext, context, updateBlockedRoute, updateLocation,
|
|
6
|
+
export declare const Provider: ({ children, setContext, context, updateBlockedRoute, updateLocation, setSearch, prefetchLoader, blockerState, routeItemData, restoreScroll, currentLoaderFallback, }: ProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
export {};
|
package/dist/types/global.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComponentType, Dispatch, ReactElement, SetStateAction } from 'react';
|
|
1
|
+
import type { ComponentType, Dispatch, ReactElement, RefObject, SetStateAction } from 'react';
|
|
2
2
|
export type LazyComponent = () => Promise<{
|
|
3
3
|
default: ComponentType<unknown>;
|
|
4
4
|
}>;
|
|
@@ -48,19 +48,19 @@ export type UpdateBlockedRouteProps = {
|
|
|
48
48
|
export type RevalidateCacheArgs = {
|
|
49
49
|
pathname: string;
|
|
50
50
|
routeItem?: RouteItem;
|
|
51
|
-
|
|
51
|
+
loaderState?: RefObject<LoaderState>;
|
|
52
52
|
};
|
|
53
|
-
export type LoaderState =
|
|
53
|
+
export type LoaderState = {
|
|
54
54
|
data: unknown;
|
|
55
55
|
loaderError: Error | null;
|
|
56
56
|
beforeLoadError: Error | null;
|
|
57
|
-
}
|
|
57
|
+
};
|
|
58
58
|
export type Adapter<T> = {
|
|
59
59
|
parse: (params: string[]) => T;
|
|
60
60
|
serialize?: (params: T) => string | string[];
|
|
61
61
|
};
|
|
62
|
-
export type
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
export type RouteItemData = {
|
|
63
|
+
location: Location;
|
|
64
|
+
routeItem: RouteItem | undefined;
|
|
65
|
+
loaderState: LoaderState;
|
|
66
66
|
};
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const RouterViewContext: import("react").Context<Record<string, string>>;
|