clear-react-router 1.7.7 → 1.7.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 +4 -0
- package/dist/components/Router.d.ts +1 -1
- package/dist/config/routerConfig.d.ts +3 -1
- package/dist/index.js +68 -38
- 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
|
@@ -271,8 +271,8 @@ var revalidateCache = ({ routeItem, pathname }) => {
|
|
|
271
271
|
return promise;
|
|
272
272
|
};
|
|
273
273
|
//#endregion
|
|
274
|
-
//#region utils/
|
|
275
|
-
var
|
|
274
|
+
//#region utils/commitState.ts
|
|
275
|
+
var commitState = (nextLocation, routeItem) => {
|
|
276
276
|
routeItemDataState.setState({
|
|
277
277
|
routeItem,
|
|
278
278
|
location: nextLocation
|
|
@@ -294,6 +294,8 @@ var RouterConfig = class {
|
|
|
294
294
|
_defineProperty(this, "isAnimated", false);
|
|
295
295
|
_defineProperty(this, "showFallbackOnAnimation", false);
|
|
296
296
|
_defineProperty(this, "hoverPrefetchDelay", 150);
|
|
297
|
+
_defineProperty(this, "beforeLoad", void 0);
|
|
298
|
+
_defineProperty(this, "afterLoad", void 0);
|
|
297
299
|
}
|
|
298
300
|
configure(config) {
|
|
299
301
|
Object.assign(this, config);
|
|
@@ -301,17 +303,17 @@ var RouterConfig = class {
|
|
|
301
303
|
};
|
|
302
304
|
var routerConfig = new RouterConfig();
|
|
303
305
|
//#endregion
|
|
304
|
-
//#region utils/
|
|
305
|
-
var
|
|
306
|
+
//#region utils/commitNavigation.ts
|
|
307
|
+
var commitNavigation = (nextLocation, routeItem) => {
|
|
306
308
|
const { isAnimated } = routerConfig;
|
|
307
309
|
if (!isAnimated || !prevPathnameRef.value) {
|
|
308
|
-
|
|
310
|
+
commitState(nextLocation, routeItem);
|
|
309
311
|
return;
|
|
310
312
|
}
|
|
311
313
|
try {
|
|
312
|
-
document.startViewTransition(() =>
|
|
314
|
+
document.startViewTransition(() => commitState(nextLocation, routeItem));
|
|
313
315
|
} catch {
|
|
314
|
-
|
|
316
|
+
commitState(nextLocation, routeItem);
|
|
315
317
|
}
|
|
316
318
|
};
|
|
317
319
|
var findRoute = (pathname, includeAll) => {
|
|
@@ -321,21 +323,24 @@ var findRoute = (pathname, includeAll) => {
|
|
|
321
323
|
//#endregion
|
|
322
324
|
//#region runtime/navigate.ts
|
|
323
325
|
var navigationSeq = 0;
|
|
324
|
-
var
|
|
325
|
-
navigationSeq = navigationSeq + 1;
|
|
326
|
-
const seq = navigationSeq;
|
|
326
|
+
var routeResolve = (location) => {
|
|
327
327
|
loaderStateRef.set(emptyLoaderState);
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
params:
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
328
|
+
const nextItem = findRoute(location.pathname, true);
|
|
329
|
+
return {
|
|
330
|
+
nextItem,
|
|
331
|
+
params: getParamsObject({
|
|
332
|
+
params: nextItem?.params,
|
|
333
|
+
pathname: location.pathname
|
|
334
|
+
})
|
|
335
|
+
};
|
|
336
|
+
};
|
|
337
|
+
var beforeLoad = async (routeItem, params) => {
|
|
338
|
+
const { beforeLoad } = routerConfig;
|
|
339
|
+
const runBeforeLoad = async (loaderFn) => {
|
|
340
|
+
const redirect = async (redirected) => await navigate(typeof redirected === "string" ? { pathname: redirected } : redirected);
|
|
341
|
+
const { context, setContext } = getContext();
|
|
337
342
|
try {
|
|
338
|
-
await
|
|
343
|
+
await loaderFn({
|
|
339
344
|
context,
|
|
340
345
|
redirect,
|
|
341
346
|
params,
|
|
@@ -350,10 +355,13 @@ var navigate = async (nextLocation) => {
|
|
|
350
355
|
...prev,
|
|
351
356
|
beforeLoadError: error
|
|
352
357
|
}));
|
|
353
|
-
return transitionedNavigation(nextLocation, nextItem);
|
|
354
358
|
}
|
|
355
|
-
}
|
|
356
|
-
if (
|
|
359
|
+
};
|
|
360
|
+
if (beforeLoad) await runBeforeLoad(beforeLoad);
|
|
361
|
+
if (routeItem?.beforeLoad) await runBeforeLoad(routeItem?.beforeLoad);
|
|
362
|
+
};
|
|
363
|
+
var prepareNavigation = (routeItem, location) => {
|
|
364
|
+
const { isAnimated, showFallbackOnAnimation: showFallback } = routerConfig;
|
|
357
365
|
scrollMapState.setState((prevState) => {
|
|
358
366
|
const scrollPosition = document.scrollingElement?.scrollTop ?? 0;
|
|
359
367
|
if (!scrollPosition || prevState[prevPathnameRef.value] === scrollPosition) return prevState;
|
|
@@ -363,24 +371,44 @@ var navigate = async (nextLocation) => {
|
|
|
363
371
|
};
|
|
364
372
|
});
|
|
365
373
|
loaderFallbackState.setState(isCacheItemFresh({
|
|
366
|
-
routeItem
|
|
367
|
-
pathname:
|
|
368
|
-
}) || isAnimated && !showFallback ? void 0 :
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
374
|
+
routeItem,
|
|
375
|
+
pathname: location.pathname
|
|
376
|
+
}) || isAnimated && !showFallback ? void 0 : routeItem?.loaderFallback);
|
|
377
|
+
};
|
|
378
|
+
var loader = async (routeItem, location) => {
|
|
379
|
+
if (!routeItem?.loader) return;
|
|
380
|
+
isLoadingState.setState(true);
|
|
381
|
+
await revalidateCache({
|
|
382
|
+
routeItem,
|
|
383
|
+
pathname: location.pathname
|
|
384
|
+
});
|
|
385
|
+
};
|
|
386
|
+
var afterLoad = async (routeItem, params) => {
|
|
387
|
+
const { afterLoad } = routerConfig;
|
|
388
|
+
const { context, setContext } = getContext();
|
|
389
|
+
if (routeItem?.afterLoad) await routeItem.afterLoad({
|
|
390
|
+
context,
|
|
391
|
+
params,
|
|
392
|
+
setContext
|
|
393
|
+
});
|
|
394
|
+
if (afterLoad) await afterLoad({
|
|
379
395
|
context,
|
|
380
396
|
params,
|
|
381
397
|
setContext
|
|
382
398
|
});
|
|
383
399
|
};
|
|
400
|
+
var navigate = async (nextLocation) => {
|
|
401
|
+
navigationSeq = navigationSeq + 1;
|
|
402
|
+
const seq = navigationSeq;
|
|
403
|
+
const { nextItem, params } = routeResolve(nextLocation);
|
|
404
|
+
await beforeLoad(nextItem, params);
|
|
405
|
+
if (seq !== navigationSeq) return;
|
|
406
|
+
prepareNavigation(nextItem, nextLocation);
|
|
407
|
+
await loader(nextItem, nextLocation);
|
|
408
|
+
if (seq !== navigationSeq) return;
|
|
409
|
+
commitNavigation(nextLocation, nextItem);
|
|
410
|
+
await afterLoad(nextItem, params);
|
|
411
|
+
};
|
|
384
412
|
//#endregion
|
|
385
413
|
//#region hooks/useNavigation.ts
|
|
386
414
|
var useNavigation = () => {
|
|
@@ -490,7 +518,7 @@ var renderElement = (Component) => {
|
|
|
490
518
|
//#endregion
|
|
491
519
|
//#region components/Router.tsx
|
|
492
520
|
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 }) => {
|
|
521
|
+
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
522
|
const [isLoading] = useIsLoading();
|
|
495
523
|
const [currentLoaderFallback] = useLoaderFallback();
|
|
496
524
|
const [routeItemData] = useRouteItemData();
|
|
@@ -501,7 +529,9 @@ var Router = ({ routes, animationDuration, isAnimated = false, spinner = true, p
|
|
|
501
529
|
isAnimated,
|
|
502
530
|
prefetch,
|
|
503
531
|
hoverPrefetchDelay,
|
|
504
|
-
showFallbackOnAnimation
|
|
532
|
+
showFallbackOnAnimation,
|
|
533
|
+
beforeLoad,
|
|
534
|
+
afterLoad
|
|
505
535
|
});
|
|
506
536
|
useApplyCustomAnimation(animationDuration);
|
|
507
537
|
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