clear-react-router 1.7.7 → 1.7.9
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 +4 -0
- package/dist/components/Router.d.ts +1 -1
- package/dist/config/routerConfig.d.ts +3 -1
- package/dist/index.js +70 -44
- package/dist/types/global.d.ts +14 -6
- package/dist/utils/commitNavigation.d.ts +2 -0
- package/dist/utils/commitState.d.ts +2 -0
- package/package.json +1 -1
- package/dist/utils/navigationExecutor.d.ts +0 -2
- package/dist/utils/transitionedNavigation.d.ts +0 -2
package/README.md
CHANGED
|
@@ -64,6 +64,8 @@ Normalizes route configuration. Handles wildcard `*` routes, extracts dynamic pa
|
|
|
64
64
|
| `animationDuration` | `number` | `optional` | Animation duration in milliseconds (browser default is used if not set) |
|
|
65
65
|
| `defaultLoaderFallback` | `ReactElement \| () => ReactElement` | `optional` | Default loading fallback for every route loader |
|
|
66
66
|
| `defaultErrorElement` | `ReactElement \| () => ReactElement` | `optional` | Default error fallback for every route |
|
|
67
|
+
| `beforeLoad` | `({ params, context, redirect, setContext }) => Promise<unknown> \| undefined \| void` | `undefined` | Runs before every navigation. Useful for authentication, analytics, or updating shared context. |
|
|
68
|
+
| `afterLoad` | `({ params, context, setContext }) => Promise<void>` | `undefined` | Runs after every successful navigation. Useful for analytics, page tracking, or other global side effects. |
|
|
67
69
|
| `spinner` | `boolean \| undefined` | `true` | Show a small spinner in the corner while loading data (only when `isAnimated` is enabled) |
|
|
68
70
|
| `preserveScroll` | `boolean \| undefined` | `true` | Save and restore scroll position when navigating between pages |
|
|
69
71
|
| `showFallbackOnAnimation` | `boolean \| undefined` | `false` | Show `loaderFallback` even when `isAnimated` is `true` (instead of spinner) |
|
|
@@ -72,6 +74,8 @@ Normalizes route configuration. Handles wildcard `*` routes, extracts dynamic pa
|
|
|
72
74
|
| `context` | `object` | `{}` | Initial context (user, theme, etc.) |
|
|
73
75
|
| `errorBoundary` | `ComponentType<{ children: ReactNode }>` | `undefined` | Custom error boundary component for catching render errors in route components |
|
|
74
76
|
|
|
77
|
+
> **Note:** Global lifecycle hooks wrap every route navigation. The global beforeLoad runs **before** the route-specific beforeLoad, while the global afterLoad runs **after** the route-specific afterLoad.
|
|
78
|
+
|
|
75
79
|
```tsx
|
|
76
80
|
<div>
|
|
77
81
|
<Navbar />
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { RouterProps } from '../types/global';
|
|
2
|
-
export declare const Router: ({ routes, animationDuration, isAnimated, spinner, preserveScroll, showFallbackOnAnimation, prefetch, hoverPrefetchDelay, errorBoundary: ErrorBoundary, context: initialContext, defaultLoaderFallback, defaultErrorElement, }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
2
|
+
export declare const Router: ({ routes, beforeLoad, afterLoad, animationDuration, isAnimated, spinner, preserveScroll, showFallbackOnAnimation, prefetch, hoverPrefetchDelay, errorBoundary: ErrorBoundary, context: initialContext, defaultLoaderFallback, defaultErrorElement, }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { RouterProps } from '../types/global';
|
|
1
|
+
import { ClientRouteItem, RouterProps } from '../types/global';
|
|
2
2
|
declare class RouterConfig {
|
|
3
3
|
routes: RouterProps['routes'];
|
|
4
4
|
prefetch: RouterProps['prefetch'];
|
|
5
5
|
isAnimated: RouterProps['isAnimated'];
|
|
6
6
|
showFallbackOnAnimation: RouterProps['showFallbackOnAnimation'];
|
|
7
7
|
hoverPrefetchDelay: number;
|
|
8
|
+
beforeLoad?: ClientRouteItem['beforeLoad'];
|
|
9
|
+
afterLoad?: ClientRouteItem['afterLoad'];
|
|
8
10
|
configure(config: Partial<RouterConfig>): void;
|
|
9
11
|
}
|
|
10
12
|
export declare const routerConfig: RouterConfig;
|
package/dist/index.js
CHANGED
|
@@ -210,6 +210,7 @@ var parseWindowLocation = (location) => ({
|
|
|
210
210
|
search: location.search
|
|
211
211
|
});
|
|
212
212
|
var comparePaths = (el, pathname) => {
|
|
213
|
+
if (!el.params?.length) return el.path.replaceAll("/", "") === pathname.replaceAll("/", "");
|
|
213
214
|
const splitElementPath = el.path.split("/").filter(Boolean);
|
|
214
215
|
const paramsLength = el.params ? Object.keys(el.params).length : 0;
|
|
215
216
|
const splitPathname = pathname.split("/").filter(Boolean);
|
|
@@ -271,8 +272,8 @@ var revalidateCache = ({ routeItem, pathname }) => {
|
|
|
271
272
|
return promise;
|
|
272
273
|
};
|
|
273
274
|
//#endregion
|
|
274
|
-
//#region utils/
|
|
275
|
-
var
|
|
275
|
+
//#region utils/commitState.ts
|
|
276
|
+
var commitState = (nextLocation, routeItem) => {
|
|
276
277
|
routeItemDataState.setState({
|
|
277
278
|
routeItem,
|
|
278
279
|
location: nextLocation
|
|
@@ -294,6 +295,8 @@ var RouterConfig = class {
|
|
|
294
295
|
_defineProperty(this, "isAnimated", false);
|
|
295
296
|
_defineProperty(this, "showFallbackOnAnimation", false);
|
|
296
297
|
_defineProperty(this, "hoverPrefetchDelay", 150);
|
|
298
|
+
_defineProperty(this, "beforeLoad", void 0);
|
|
299
|
+
_defineProperty(this, "afterLoad", void 0);
|
|
297
300
|
}
|
|
298
301
|
configure(config) {
|
|
299
302
|
Object.assign(this, config);
|
|
@@ -301,17 +304,17 @@ var RouterConfig = class {
|
|
|
301
304
|
};
|
|
302
305
|
var routerConfig = new RouterConfig();
|
|
303
306
|
//#endregion
|
|
304
|
-
//#region utils/
|
|
305
|
-
var
|
|
307
|
+
//#region utils/commitNavigation.ts
|
|
308
|
+
var commitNavigation = (nextLocation, routeItem) => {
|
|
306
309
|
const { isAnimated } = routerConfig;
|
|
307
310
|
if (!isAnimated || !prevPathnameRef.value) {
|
|
308
|
-
|
|
311
|
+
commitState(nextLocation, routeItem);
|
|
309
312
|
return;
|
|
310
313
|
}
|
|
311
314
|
try {
|
|
312
|
-
document.startViewTransition(() =>
|
|
315
|
+
document.startViewTransition(() => commitState(nextLocation, routeItem));
|
|
313
316
|
} catch {
|
|
314
|
-
|
|
317
|
+
commitState(nextLocation, routeItem);
|
|
315
318
|
}
|
|
316
319
|
};
|
|
317
320
|
var findRoute = (pathname, includeAll) => {
|
|
@@ -321,25 +324,26 @@ var findRoute = (pathname, includeAll) => {
|
|
|
321
324
|
//#endregion
|
|
322
325
|
//#region runtime/navigate.ts
|
|
323
326
|
var navigationSeq = 0;
|
|
324
|
-
var
|
|
325
|
-
navigationSeq = navigationSeq + 1;
|
|
326
|
-
const seq = navigationSeq;
|
|
327
|
+
var routeResolve = (location) => {
|
|
327
328
|
loaderStateRef.set(emptyLoaderState);
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
params:
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
329
|
+
const nextItem = findRoute(location.pathname, true);
|
|
330
|
+
return {
|
|
331
|
+
nextItem,
|
|
332
|
+
params: getParamsObject({
|
|
333
|
+
params: nextItem?.params,
|
|
334
|
+
pathname: location.pathname
|
|
335
|
+
})
|
|
336
|
+
};
|
|
337
|
+
};
|
|
338
|
+
var beforeLoad = async (routeItem, params) => {
|
|
339
|
+
const { beforeLoad } = routerConfig;
|
|
340
|
+
const runBeforeLoad = async (loaderFn) => {
|
|
341
|
+
const redirect = async (redirected) => await navigate(typeof redirected === "string" ? { pathname: redirected } : redirected);
|
|
337
342
|
try {
|
|
338
|
-
await
|
|
339
|
-
|
|
343
|
+
await loaderFn({
|
|
344
|
+
...getContext(),
|
|
340
345
|
redirect,
|
|
341
|
-
params
|
|
342
|
-
setContext
|
|
346
|
+
params
|
|
343
347
|
});
|
|
344
348
|
loaderStateRef.set((prev) => ({
|
|
345
349
|
...prev,
|
|
@@ -350,10 +354,13 @@ var navigate = async (nextLocation) => {
|
|
|
350
354
|
...prev,
|
|
351
355
|
beforeLoadError: error
|
|
352
356
|
}));
|
|
353
|
-
return transitionedNavigation(nextLocation, nextItem);
|
|
354
357
|
}
|
|
355
|
-
}
|
|
356
|
-
if (
|
|
358
|
+
};
|
|
359
|
+
if (beforeLoad) await runBeforeLoad(beforeLoad);
|
|
360
|
+
if (routeItem?.beforeLoad) await runBeforeLoad(routeItem?.beforeLoad);
|
|
361
|
+
};
|
|
362
|
+
var prepareNavigation = (routeItem, location) => {
|
|
363
|
+
const { isAnimated, showFallbackOnAnimation: showFallback } = routerConfig;
|
|
357
364
|
scrollMapState.setState((prevState) => {
|
|
358
365
|
const scrollPosition = document.scrollingElement?.scrollTop ?? 0;
|
|
359
366
|
if (!scrollPosition || prevState[prevPathnameRef.value] === scrollPosition) return prevState;
|
|
@@ -363,24 +370,41 @@ var navigate = async (nextLocation) => {
|
|
|
363
370
|
};
|
|
364
371
|
});
|
|
365
372
|
loaderFallbackState.setState(isCacheItemFresh({
|
|
366
|
-
routeItem
|
|
367
|
-
pathname:
|
|
368
|
-
}) || isAnimated && !showFallback ? void 0 :
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (seq !== navigationSeq) return;
|
|
377
|
-
transitionedNavigation(nextLocation, nextItem);
|
|
378
|
-
if (nextItem?.afterLoad) await nextItem.afterLoad({
|
|
379
|
-
context,
|
|
380
|
-
params,
|
|
381
|
-
setContext
|
|
373
|
+
routeItem,
|
|
374
|
+
pathname: location.pathname
|
|
375
|
+
}) || isAnimated && !showFallback ? void 0 : routeItem?.loaderFallback);
|
|
376
|
+
};
|
|
377
|
+
var loader = async (routeItem, location) => {
|
|
378
|
+
if (!routeItem?.loader) return;
|
|
379
|
+
isLoadingState.setState(true);
|
|
380
|
+
await revalidateCache({
|
|
381
|
+
routeItem,
|
|
382
|
+
pathname: location.pathname
|
|
382
383
|
});
|
|
383
384
|
};
|
|
385
|
+
var afterLoad = async (routeItem, params) => {
|
|
386
|
+
const { afterLoad } = routerConfig;
|
|
387
|
+
if (routeItem?.afterLoad) await routeItem.afterLoad({
|
|
388
|
+
...getContext(),
|
|
389
|
+
params
|
|
390
|
+
});
|
|
391
|
+
if (afterLoad) await afterLoad({
|
|
392
|
+
...getContext(),
|
|
393
|
+
params
|
|
394
|
+
});
|
|
395
|
+
};
|
|
396
|
+
var navigate = async (nextLocation) => {
|
|
397
|
+
navigationSeq = navigationSeq + 1;
|
|
398
|
+
const seq = navigationSeq;
|
|
399
|
+
const { nextItem, params } = routeResolve(nextLocation);
|
|
400
|
+
await beforeLoad(nextItem, params);
|
|
401
|
+
if (seq !== navigationSeq) return;
|
|
402
|
+
prepareNavigation(nextItem, nextLocation);
|
|
403
|
+
await loader(nextItem, nextLocation);
|
|
404
|
+
if (seq !== navigationSeq) return;
|
|
405
|
+
commitNavigation(nextLocation, nextItem);
|
|
406
|
+
await afterLoad(nextItem, params);
|
|
407
|
+
};
|
|
384
408
|
//#endregion
|
|
385
409
|
//#region hooks/useNavigation.ts
|
|
386
410
|
var useNavigation = () => {
|
|
@@ -490,7 +514,7 @@ var renderElement = (Component) => {
|
|
|
490
514
|
//#endregion
|
|
491
515
|
//#region components/Router.tsx
|
|
492
516
|
var EmptyBoundary = ({ children }) => children;
|
|
493
|
-
var Router = ({ routes, animationDuration, isAnimated = false, spinner = true, preserveScroll = true, showFallbackOnAnimation = false, prefetch = "hover", hoverPrefetchDelay = 150, errorBoundary: ErrorBoundary = EmptyBoundary, context: initialContext, defaultLoaderFallback, defaultErrorElement }) => {
|
|
517
|
+
var Router = ({ routes, beforeLoad, afterLoad, animationDuration, isAnimated = false, spinner = true, preserveScroll = true, showFallbackOnAnimation = false, prefetch = "hover", hoverPrefetchDelay = 150, errorBoundary: ErrorBoundary = EmptyBoundary, context: initialContext, defaultLoaderFallback, defaultErrorElement }) => {
|
|
494
518
|
const [isLoading] = useIsLoading();
|
|
495
519
|
const [currentLoaderFallback] = useLoaderFallback();
|
|
496
520
|
const [routeItemData] = useRouteItemData();
|
|
@@ -501,7 +525,9 @@ var Router = ({ routes, animationDuration, isAnimated = false, spinner = true, p
|
|
|
501
525
|
isAnimated,
|
|
502
526
|
prefetch,
|
|
503
527
|
hoverPrefetchDelay,
|
|
504
|
-
showFallbackOnAnimation
|
|
528
|
+
showFallbackOnAnimation,
|
|
529
|
+
beforeLoad,
|
|
530
|
+
afterLoad
|
|
505
531
|
});
|
|
506
532
|
useApplyCustomAnimation(animationDuration);
|
|
507
533
|
useSetInitialContext(initialContext);
|
package/dist/types/global.d.ts
CHANGED
|
@@ -3,6 +3,17 @@ export type LazyComponent = () => Promise<{
|
|
|
3
3
|
default: ComponentType<unknown>;
|
|
4
4
|
}>;
|
|
5
5
|
export type RenderElement = (() => ReactElement) | ReactElement;
|
|
6
|
+
export type BeforeLoad = (arg: {
|
|
7
|
+
context: Record<string, unknown>;
|
|
8
|
+
redirect: (arg: Location | string) => Promise<void>;
|
|
9
|
+
params: Record<string, string>;
|
|
10
|
+
setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
|
|
11
|
+
}) => Promise<unknown> | undefined | void;
|
|
12
|
+
export type AfterLoad = (arg: {
|
|
13
|
+
context: Record<string, unknown>;
|
|
14
|
+
params: Record<string, string>;
|
|
15
|
+
setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
|
|
16
|
+
}) => Promise<void>;
|
|
6
17
|
export type ClientRouteItem = {
|
|
7
18
|
path: string;
|
|
8
19
|
element: RenderElement | LazyComponent;
|
|
@@ -16,12 +27,7 @@ export type ClientRouteItem = {
|
|
|
16
27
|
fallback?: RenderElement;
|
|
17
28
|
children?: ClientRouteItem[];
|
|
18
29
|
staleTime?: number;
|
|
19
|
-
beforeLoad?:
|
|
20
|
-
context: Record<string, unknown>;
|
|
21
|
-
redirect: (arg: Location | string) => Promise<void>;
|
|
22
|
-
params: Record<string, string>;
|
|
23
|
-
setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
|
|
24
|
-
}) => Promise<unknown> | undefined | void;
|
|
30
|
+
beforeLoad?: BeforeLoad;
|
|
25
31
|
afterLoad?: (arg: {
|
|
26
32
|
context: Record<string, unknown>;
|
|
27
33
|
params: Record<string, string>;
|
|
@@ -79,5 +85,7 @@ export type RouterProps = {
|
|
|
79
85
|
errorBoundary?: ComponentType<{
|
|
80
86
|
children: ReactNode;
|
|
81
87
|
}>;
|
|
88
|
+
beforeLoad?: ClientRouteItem['beforeLoad'];
|
|
89
|
+
afterLoad?: ClientRouteItem['afterLoad'];
|
|
82
90
|
context?: Record<string, unknown>;
|
|
83
91
|
};
|
package/package.json
CHANGED