clear-react-router 2.0.5 → 2.0.6
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 +3 -12
- package/dist/components/Router.d.ts +1 -1
- package/dist/config/routerConfig.d.ts +1 -0
- package/dist/index.js +44 -47
- package/dist/types.d.ts +5 -2
- package/dist/utils/commitNavigation.d.ts +1 -3
- package/dist/utils/commitState.d.ts +1 -1
- package/dist/utils/utils.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,10 +46,10 @@ It provides first-class support for:
|
|
|
46
46
|
| `maxCacheSize` | `number \| undefined` | 60 for mobile, 150 for desktop | Maximum number of cached loader entries. Once the limit is reached, the least recently used entries are evicted |
|
|
47
47
|
| `isAnimated` | `boolean \| undefined` | `false` | Enable smooth page fade transitions |
|
|
48
48
|
| `animationDuration` | `number` | `optional` | Animation duration in milliseconds (browser default is used if not set) |
|
|
49
|
-
| `
|
|
49
|
+
| `optimisticSpinner` | `boolean \| undefined` | `true` | Show a small spinner in the corner while optimistic route data revalidates in the background |
|
|
50
50
|
| `context` | `object` | `{}` | Initial context (user, theme, etc.) |
|
|
51
51
|
| `errorBoundary` | `ComponentType<{ children: ReactNode }>` | `undefined` | Custom error boundary component for catching render errors in route components |
|
|
52
|
-
| `
|
|
52
|
+
| `defaultMinLoaderDuration` | `number \| undefined` | `0` | Default minimum time the loader fallback stays visible, to avoid flickering |
|
|
53
53
|
| `defaultLoaderFallback` | `ReactElement \| () => ReactElement` | `optional` | Default loading fallback for every route loader |
|
|
54
54
|
| `defaultErrorElement` | `ReactElement \| () => ReactElement` | `optional` | Default error fallback for every route |
|
|
55
55
|
| `defaultRetry` | `number \| { count: number; delay: number }` | `optional` | Default cache revalidation retry policy for all routes |
|
|
@@ -63,14 +63,6 @@ It provides first-class support for:
|
|
|
63
63
|
|
|
64
64
|
> **Note:** Global lifecycle hooks wrap every route navigation. The global `defaultBeforeLoad` runs **before** the route-specific beforeLoad, while the global `defaultAfterLoad` runs **after** the route-specific afterLoad.
|
|
65
65
|
|
|
66
|
-
```tsx
|
|
67
|
-
<div>
|
|
68
|
-
<Navbar />
|
|
69
|
-
<Router routes={routes} spinner={false} isAnimated /> {/* disable the spinner */}
|
|
70
|
-
</div>
|
|
71
|
-
```
|
|
72
|
-
> **Note:** When `isAnimated` is enabled, `loaderFallback` is not shown. Instead, a small spinner appears (if `spinner={true}`). On the initial page load, however, the route's loaderFallback is rendered if available.
|
|
73
|
-
|
|
74
66
|
### `createRouter(routes)`
|
|
75
67
|
|
|
76
68
|
Normalizes route configuration. Extracts dynamic params, builds nested paths.
|
|
@@ -82,6 +74,7 @@ Normalizes route configuration. Extracts dynamic params, builds nested paths.
|
|
|
82
74
|
| `beforeLoad` | `({ params, context, redirect, setContext }) => Promise<unknown> \| undefined \| void` | Runs before every route navigation. Auth checks and redirects. Can update context via `setContext`. `redirect` is provided by the router |
|
|
83
75
|
| `loader` | `({ params, context, setContext, searchParams, signal }) => Promise<unknown>` | Fetch data using route params, search params, abort controller signal and context. Can update context via `setContext` |
|
|
84
76
|
| `afterLoad` | `({ params, context, setContext }) => Promise<void>` | Runs after a successful navigation once the route has finished loading. Analytics, side effects after data is loaded. Can update context via `setContext` |
|
|
77
|
+
| `minLoaderDuration` | `number \| undefined` | `undefined` | Minimum time the loader fallback stays visible, to avoid flickering |
|
|
85
78
|
| `fallback` | `ReactElement \| () => ReactElement` | Loading fallback (for lazy loading) |
|
|
86
79
|
| `loaderFallback` | `ReactElement \| () => ReactElement` | Loading fallback for the route's `loader`. Overrides the global `defaultLoaderFallback` set in `Router` |
|
|
87
80
|
| `retry` | `number \| { count: number; delay: number }` | Overrides the global cache revalidation retry policy for this route |
|
|
@@ -816,8 +809,6 @@ Clear Router supports smooth page transitions using the native View Transitions
|
|
|
816
809
|
## How It Works
|
|
817
810
|
|
|
818
811
|
- **Data loads first** — All `loader` and `beforeLoad` hooks complete before animation starts
|
|
819
|
-
- **No `loaderFallback`** — The `loaderFallback` is not shown during animated transitions
|
|
820
|
-
- **Subtle spinner** — A small spinner appears in the top-left corner while data is loading and `spinner` prop of `Router` component is on, so users know the app is responsive
|
|
821
812
|
- **Native API** — Uses `document.startViewTransition` for smooth, hardware-accelerated animations
|
|
822
813
|
|
|
823
814
|
## Browser Support
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { RouterProps } from '../types';
|
|
2
|
-
export declare const Router: ({ routes, defaultBeforeLoad, defaultAfterLoad, animationDuration, defaultLoaderFallback, defaultErrorElement, defaultRetry, defaultStaleTime, context: initialContext, isAnimated,
|
|
2
|
+
export declare const Router: ({ routes, defaultBeforeLoad, defaultAfterLoad, animationDuration, defaultLoaderFallback, defaultErrorElement, defaultRetry, defaultStaleTime, context: initialContext, isAnimated, optimisticSpinner, defaultPreserveScroll, defaultMinLoaderDuration, maxCacheSize, defaultPrefetch, defaultHoverPrefetchDelay, errorBoundary: ErrorBoundary, }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -10,6 +10,7 @@ declare class RouterConfig {
|
|
|
10
10
|
defaultRetry?: RouterProps['defaultRetry'];
|
|
11
11
|
defaultStaleTime?: RouterProps['defaultStaleTime'];
|
|
12
12
|
defaultPreserveScroll?: RouterProps['defaultPreserveScroll'];
|
|
13
|
+
defaultMinLoaderDuration?: RouterProps['defaultMinLoaderDuration'];
|
|
13
14
|
configure(config: Partial<RouterConfig>): void;
|
|
14
15
|
}
|
|
15
16
|
export declare const routerConfig: RouterConfig;
|
package/dist/index.js
CHANGED
|
@@ -30,12 +30,13 @@ var useGlobalState = ({ subscribe, getState, setState }) => {
|
|
|
30
30
|
};
|
|
31
31
|
//#endregion
|
|
32
32
|
//#region utils/commitState.ts
|
|
33
|
-
var createCommitState = ({ routeItemDataState, pendingState }) => (nextLocation, routeItem) => {
|
|
33
|
+
var createCommitState = ({ routeItemDataState, pendingState, isOptimisticLoading }) => (nextLocation, routeItem) => {
|
|
34
34
|
routeItemDataState.setState({
|
|
35
35
|
routeItem,
|
|
36
36
|
location: nextLocation
|
|
37
37
|
});
|
|
38
38
|
pendingState.setState(void 0);
|
|
39
|
+
isOptimisticLoading.setState(false);
|
|
39
40
|
const fullPath = nextLocation.search ? `${nextLocation.pathname}${nextLocation.search}` : nextLocation.pathname;
|
|
40
41
|
if (fullPath === window.location.pathname + window.location.search) return;
|
|
41
42
|
history.pushState(null, "", fullPath);
|
|
@@ -95,6 +96,7 @@ var RouterConfig = class {
|
|
|
95
96
|
_defineProperty(this, "defaultRetry", void 0);
|
|
96
97
|
_defineProperty(this, "defaultStaleTime", void 0);
|
|
97
98
|
_defineProperty(this, "defaultPreserveScroll", void 0);
|
|
99
|
+
_defineProperty(this, "defaultMinLoaderDuration", void 0);
|
|
98
100
|
}
|
|
99
101
|
configure(config) {
|
|
100
102
|
Object.assign(this, config);
|
|
@@ -103,13 +105,12 @@ var RouterConfig = class {
|
|
|
103
105
|
var routerConfig = new RouterConfig();
|
|
104
106
|
//#endregion
|
|
105
107
|
//#region utils/commitNavigation.ts
|
|
106
|
-
var
|
|
107
|
-
|
|
108
|
-
if (!routerConfig.isAnimated || isFirstLoad) return navigationExecutor(nextLocation, routeItem);
|
|
108
|
+
var commitNavigation = (callback) => {
|
|
109
|
+
if (!routerConfig.isAnimated) return callback();
|
|
109
110
|
try {
|
|
110
|
-
document.startViewTransition(
|
|
111
|
+
document.startViewTransition(callback);
|
|
111
112
|
} catch {
|
|
112
|
-
|
|
113
|
+
callback();
|
|
113
114
|
}
|
|
114
115
|
};
|
|
115
116
|
//#endregion
|
|
@@ -133,7 +134,7 @@ var createIsCacheItemFresh = (loaderMap) => (path) => {
|
|
|
133
134
|
* LICENSE file in the root directory of this source tree.
|
|
134
135
|
*/
|
|
135
136
|
var require_react_jsx_runtime_production = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
136
|
-
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element")
|
|
137
|
+
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element");
|
|
137
138
|
function jsxProd(type, config, maybeKey) {
|
|
138
139
|
var key = null;
|
|
139
140
|
void 0 !== maybeKey && (key = "" + maybeKey);
|
|
@@ -151,7 +152,6 @@ var require_react_jsx_runtime_production = /* @__PURE__ */ __commonJSMin(((expor
|
|
|
151
152
|
props: maybeKey
|
|
152
153
|
};
|
|
153
154
|
}
|
|
154
|
-
exports.Fragment = REACT_FRAGMENT_TYPE;
|
|
155
155
|
exports.jsx = jsxProd;
|
|
156
156
|
exports.jsxs = jsxProd;
|
|
157
157
|
}));
|
|
@@ -215,6 +215,7 @@ var isMobile = () => {
|
|
|
215
215
|
const isSmallScreen = window.matchMedia("(max-width: 768px)").matches;
|
|
216
216
|
return hasCoarsePointer && isSmallScreen;
|
|
217
217
|
};
|
|
218
|
+
var sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
218
219
|
var findRoute = (pathname, includeAll) => {
|
|
219
220
|
if (includeAll) return routerConfig.routes.find((el) => el.path === "*" || comparePaths(el, pathname));
|
|
220
221
|
return routerConfig.routes.find((el) => comparePaths(el, pathname));
|
|
@@ -225,14 +226,15 @@ var createNavigate = (routerState, revalidateCache) => {
|
|
|
225
226
|
let navigationSeq = 0;
|
|
226
227
|
let interval = 0;
|
|
227
228
|
let abortController = null;
|
|
228
|
-
const { loaderStateRef, scrollMapState, pendingState, contextState, loaderMap, routeItemDataState } = routerState;
|
|
229
|
-
const
|
|
229
|
+
const { loaderStateRef, scrollMapState, pendingState, contextState, loaderMap, routeItemDataState, isOptimisticLoading } = routerState;
|
|
230
|
+
const navigationExecutor = createCommitState(routerState);
|
|
230
231
|
const isCacheItemFresh = createIsCacheItemFresh(loaderMap);
|
|
231
232
|
const createSignal = () => {
|
|
232
233
|
abortController?.abort();
|
|
233
234
|
abortController = new AbortController();
|
|
234
235
|
return abortController.signal;
|
|
235
236
|
};
|
|
237
|
+
const getPath = (nextLocation) => `${nextLocation.pathname}${nextLocation.search ?? ""}`;
|
|
236
238
|
const getContext = () => ({
|
|
237
239
|
context: contextState.getState(),
|
|
238
240
|
setContext: contextState.setState
|
|
@@ -279,44 +281,47 @@ var createNavigate = (routerState, revalidateCache) => {
|
|
|
279
281
|
[prevPathname]: scrollPosition
|
|
280
282
|
};
|
|
281
283
|
});
|
|
282
|
-
const path =
|
|
284
|
+
const path = getPath(location);
|
|
283
285
|
if (routeItem?.optimistic && loaderMap.has(path)) {
|
|
284
286
|
routeItemDataState.setState({
|
|
285
287
|
routeItem,
|
|
286
288
|
location
|
|
287
289
|
});
|
|
290
|
+
isOptimisticLoading.setState(true);
|
|
288
291
|
const currentLoaderState = loaderMap.get(path)?.state;
|
|
289
292
|
if (currentLoaderState) loaderStateRef.set(currentLoaderState);
|
|
290
|
-
} else {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
} : void 0);
|
|
296
|
-
}
|
|
293
|
+
} else if (routeItem?.loader && !isCacheItemFresh(path)) commitNavigation(() => pendingState.setState({
|
|
294
|
+
routeItem,
|
|
295
|
+
location
|
|
296
|
+
}));
|
|
297
|
+
else pendingState.setState(void 0);
|
|
297
298
|
};
|
|
298
|
-
const polling = (routeItem,
|
|
299
|
+
const polling = (routeItem, nextLocation) => {
|
|
299
300
|
if (!routeItem?.pollingInterval) return;
|
|
300
301
|
const signal = createSignal();
|
|
301
302
|
interval = window.setInterval(() => revalidateCache({
|
|
302
303
|
routeItem,
|
|
303
|
-
pathname:
|
|
304
|
-
search:
|
|
304
|
+
pathname: nextLocation.pathname,
|
|
305
|
+
search: nextLocation.search,
|
|
305
306
|
signal
|
|
306
307
|
}), routeItem.pollingInterval);
|
|
307
308
|
};
|
|
308
|
-
const
|
|
309
|
+
const getLoaderDurationPromise = (routeItem, nextLocation) => {
|
|
310
|
+
const minLoaderDuration = routeItem?.minLoaderDuration ?? routerConfig.defaultMinLoaderDuration ?? 0;
|
|
311
|
+
return minLoaderDuration && !isCacheItemFresh(getPath(nextLocation)) ? sleep(minLoaderDuration) : Promise.resolve;
|
|
312
|
+
};
|
|
313
|
+
const loader = async (routeItem, nextLocation, seq) => {
|
|
309
314
|
if (!routeItem?.loader) return;
|
|
310
315
|
window.clearInterval(interval);
|
|
311
316
|
const signal = createSignal();
|
|
312
|
-
await revalidateCache({
|
|
317
|
+
await Promise.all([revalidateCache({
|
|
313
318
|
routeItem,
|
|
314
|
-
pathname:
|
|
315
|
-
search:
|
|
319
|
+
pathname: nextLocation.pathname,
|
|
320
|
+
search: nextLocation.search,
|
|
316
321
|
signal
|
|
317
|
-
});
|
|
322
|
+
}), getLoaderDurationPromise(routeItem, nextLocation)]);
|
|
318
323
|
if (seq !== navigationSeq) return;
|
|
319
|
-
polling(routeItem,
|
|
324
|
+
polling(routeItem, nextLocation);
|
|
320
325
|
};
|
|
321
326
|
const afterLoad = async (routeItem, params) => {
|
|
322
327
|
const { defaultAfterLoad } = routerConfig;
|
|
@@ -338,7 +343,7 @@ var createNavigate = (routerState, revalidateCache) => {
|
|
|
338
343
|
prepareNavigation(nextItem, nextLocation);
|
|
339
344
|
await loader(nextItem, nextLocation, seq);
|
|
340
345
|
if (seq !== navigationSeq) return;
|
|
341
|
-
commitNavigation(nextLocation, nextItem);
|
|
346
|
+
commitNavigation(() => navigationExecutor(nextLocation, nextItem));
|
|
342
347
|
await afterLoad(nextItem, params);
|
|
343
348
|
};
|
|
344
349
|
return navigate;
|
|
@@ -430,16 +435,8 @@ var getRetry = (routeItem) => {
|
|
|
430
435
|
delay: routeRetry ? routeRetry.delay : globalRetry?.delay || 0
|
|
431
436
|
};
|
|
432
437
|
};
|
|
433
|
-
var sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
434
438
|
var createRevalidateCache = (routerState) => {
|
|
435
439
|
const { loaderStateRef, contextState, loaderMap, loadingPromises } = routerState;
|
|
436
|
-
const removeStaleItems = () => {
|
|
437
|
-
const deletedItems = [...loaderMap.entries()].filter(([, item]) => {
|
|
438
|
-
const staleTime = item.staleTime ?? routerConfig.defaultStaleTime;
|
|
439
|
-
return staleTime && staleTime + item.timestamp < Date.now();
|
|
440
|
-
});
|
|
441
|
-
if (deletedItems.length) deletedItems.forEach((item) => loaderMap.delete(item[0]));
|
|
442
|
-
};
|
|
443
440
|
const evict = () => {
|
|
444
441
|
if (loaderMap.size <= routerConfig.maxCacheSize) return;
|
|
445
442
|
const oldestKey = loaderMap.keys().next().value;
|
|
@@ -456,7 +453,6 @@ var createRevalidateCache = (routerState) => {
|
|
|
456
453
|
const revalidateCache = async ({ routeItem, pathname, search = "", signal }, retried = 0) => {
|
|
457
454
|
if (!routeItem?.loader) return;
|
|
458
455
|
const isCacheItemFresh = createIsCacheItemFresh(loaderMap);
|
|
459
|
-
removeStaleItems();
|
|
460
456
|
const path = `${pathname}${search}`;
|
|
461
457
|
if (loadingPromises.has(path)) {
|
|
462
458
|
moveItemToLastPosition(path);
|
|
@@ -566,6 +562,7 @@ var createRouterInstance = () => {
|
|
|
566
562
|
from: "",
|
|
567
563
|
to: ""
|
|
568
564
|
}),
|
|
565
|
+
isOptimisticLoading: create(false),
|
|
569
566
|
loaderStateRef: new Cell(emptyLoaderState),
|
|
570
567
|
loaderMap: /* @__PURE__ */ new Map(),
|
|
571
568
|
loadingPromises: /* @__PURE__ */ new Map()
|
|
@@ -606,6 +603,7 @@ var createRouterInstance = () => {
|
|
|
606
603
|
useScrollMap: () => useGlobalState(routerState.scrollMapState),
|
|
607
604
|
usePendingState: () => useGlobalState(routerState.pendingState),
|
|
608
605
|
useContextState: () => useGlobalState(routerState.contextState),
|
|
606
|
+
useOptimisticLoading: () => useGlobalState(routerState.isOptimisticLoading),
|
|
609
607
|
useParams: () => getParamsObject(),
|
|
610
608
|
useNavigate: () => {
|
|
611
609
|
const { blockedRouteState } = routerState;
|
|
@@ -758,12 +756,13 @@ var EmptyBoundary = ({ children }) => children;
|
|
|
758
756
|
var IS_MOBILE = isMobile();
|
|
759
757
|
var MOBILE_CACHE_SIZE = 60;
|
|
760
758
|
var DESKTOP_CACHE_SIZE = 150;
|
|
761
|
-
var Router = ({ routes, defaultBeforeLoad, defaultAfterLoad, animationDuration, defaultLoaderFallback, defaultErrorElement, defaultRetry, defaultStaleTime, context: initialContext, isAnimated = false,
|
|
762
|
-
const { useRouteItemData, usePendingState } = router.hooks;
|
|
759
|
+
var Router = ({ routes, defaultBeforeLoad, defaultAfterLoad, animationDuration, defaultLoaderFallback, defaultErrorElement, defaultRetry, defaultStaleTime, context: initialContext, isAnimated = false, optimisticSpinner = true, defaultPreserveScroll = true, defaultMinLoaderDuration = 0, maxCacheSize = IS_MOBILE ? MOBILE_CACHE_SIZE : DESKTOP_CACHE_SIZE, defaultPrefetch = IS_MOBILE ? "viewport" : "hover", defaultHoverPrefetchDelay = 150, errorBoundary: ErrorBoundary = EmptyBoundary }) => {
|
|
760
|
+
const { useRouteItemData, usePendingState, useOptimisticLoading } = router.hooks;
|
|
763
761
|
const [routeItemData] = useRouteItemData();
|
|
764
|
-
const [
|
|
762
|
+
const [pendingRouteData] = usePendingState();
|
|
763
|
+
const [isOptimisticLoading] = useOptimisticLoading();
|
|
765
764
|
const loaderState = router.state.loaderStateRef.value;
|
|
766
|
-
const isLoading = Boolean(
|
|
765
|
+
const isLoading = Boolean(pendingRouteData);
|
|
767
766
|
useNavigation();
|
|
768
767
|
useSetRouterConfig({
|
|
769
768
|
routes,
|
|
@@ -775,6 +774,7 @@ var Router = ({ routes, defaultBeforeLoad, defaultAfterLoad, animationDuration,
|
|
|
775
774
|
defaultRetry,
|
|
776
775
|
defaultStaleTime,
|
|
777
776
|
defaultPreserveScroll,
|
|
777
|
+
defaultMinLoaderDuration,
|
|
778
778
|
maxCacheSize
|
|
779
779
|
});
|
|
780
780
|
useApplyCustomAnimation(animationDuration);
|
|
@@ -782,15 +782,12 @@ var Router = ({ routes, defaultBeforeLoad, defaultAfterLoad, animationDuration,
|
|
|
782
782
|
usePreserveScroll(routeItemData);
|
|
783
783
|
const { routeItem, location } = routeItemData;
|
|
784
784
|
const showErrorElement = !isLoading && Boolean(loaderState.loaderError || loaderState.beforeLoadError);
|
|
785
|
-
|
|
786
|
-
const loadingContent = !showErrorElement && isLoading;
|
|
787
|
-
if ((showFallbackOnAnimation || !isAnimated) && loadingContent) return renderElement(pendingState?.routeItem?.loaderFallback || defaultLoaderFallback);
|
|
788
|
-
if (!showFallbackOnAnimation && isAnimated && loadingContent) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {});
|
|
785
|
+
if (!showErrorElement && isLoading) return renderElement(pendingRouteData?.routeItem?.loaderFallback || defaultLoaderFallback);
|
|
789
786
|
if (!routeItem) return null;
|
|
790
|
-
if (showErrorElement) return
|
|
787
|
+
if (showErrorElement) return renderElement(routeItem.errorElement || defaultErrorElement);
|
|
791
788
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
792
789
|
style: { viewTransitionName: "page" },
|
|
793
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorBoundary, { children: renderElement(routeItem.element) }, location.pathname),
|
|
790
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorBoundary, { children: renderElement(routeItem.element) }, location.pathname), optimisticSpinner && isOptimisticLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})]
|
|
794
791
|
});
|
|
795
792
|
};
|
|
796
793
|
//#endregion
|
package/dist/types.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export type ClientRouteItem = {
|
|
|
33
33
|
optimistic?: boolean;
|
|
34
34
|
pollingInterval?: number;
|
|
35
35
|
retry?: Retry;
|
|
36
|
+
minLoaderDuration?: number;
|
|
36
37
|
preserveScroll?: boolean;
|
|
37
38
|
beforeLoad?: BeforeLoad;
|
|
38
39
|
afterLoad?: (arg: {
|
|
@@ -85,15 +86,15 @@ export type RouterProps = {
|
|
|
85
86
|
routes: RouteItem[];
|
|
86
87
|
isAnimated?: boolean;
|
|
87
88
|
animationDuration?: number;
|
|
88
|
-
|
|
89
|
+
optimisticSpinner?: boolean;
|
|
89
90
|
defaultPreserveScroll?: boolean;
|
|
90
91
|
defaultRetry?: Retry;
|
|
91
92
|
defaultStaleTime?: number;
|
|
92
93
|
defaultLoaderFallback?: RenderElement;
|
|
93
94
|
defaultErrorElement?: RenderElement;
|
|
94
|
-
showFallbackOnAnimation?: boolean;
|
|
95
95
|
defaultPrefetch?: 'hover' | 'render' | 'viewport' | 'none';
|
|
96
96
|
defaultHoverPrefetchDelay?: number;
|
|
97
|
+
defaultMinLoaderDuration?: number;
|
|
97
98
|
maxCacheSize?: number;
|
|
98
99
|
errorBoundary?: ComponentType<{
|
|
99
100
|
children: ReactNode;
|
|
@@ -124,6 +125,7 @@ export type RouterState = {
|
|
|
124
125
|
from: string;
|
|
125
126
|
to: string;
|
|
126
127
|
}>;
|
|
128
|
+
isOptimisticLoading: Store<boolean>;
|
|
127
129
|
loaderStateRef: Cell<LoaderState>;
|
|
128
130
|
loaderMap: Map<string, LoaderStateItem>;
|
|
129
131
|
loadingPromises: Map<string, LoadingPromise>;
|
|
@@ -144,6 +146,7 @@ export type RouterType = {
|
|
|
144
146
|
useScrollMap: () => ReturnType<typeof useGlobalState<Record<string, number>>>;
|
|
145
147
|
usePendingState: () => ReturnType<typeof useGlobalState<RouteItemData | undefined>>;
|
|
146
148
|
useContextState: () => ReturnType<typeof useGlobalState<Record<string, unknown>>>;
|
|
149
|
+
useOptimisticLoading: () => ReturnType<typeof useGlobalState<boolean>>;
|
|
147
150
|
useParams: <T>() => T;
|
|
148
151
|
useNavigate: () => (arg: Location | string | -1) => Promise<void>;
|
|
149
152
|
useGetAction: (actionKey: string) => {
|
|
@@ -1,3 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { Location, RouteItem, RouteItemData } from '../types';
|
|
3
|
-
export declare const createCommitNavigation: (navigationExecutor: (arg: Location, routeItem: RouteItem | undefined) => void, routeItemDataState: Store<RouteItemData>) => (nextLocation: Location, routeItem: RouteItem | undefined) => void;
|
|
1
|
+
export declare const commitNavigation: (callback: () => void) => void;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { Location, RouteItem, RouterState } from '../types';
|
|
2
|
-
export declare const createCommitState: ({ routeItemDataState, pendingState }: RouterState) => (nextLocation: Location, routeItem: RouteItem | undefined) => void;
|
|
2
|
+
export declare const createCommitState: ({ routeItemDataState, pendingState, isOptimisticLoading }: RouterState) => (nextLocation: Location, routeItem: RouteItem | undefined) => void;
|
package/dist/utils/utils.d.ts
CHANGED
|
@@ -4,3 +4,4 @@ export declare const getParamsObject: (nextItem?: RouteItem, nextPathname?: stri
|
|
|
4
4
|
export declare const parseWindowLocation: (location: typeof window.location) => Location;
|
|
5
5
|
export declare const comparePaths: (route: RouteItem, pathname: string) => boolean;
|
|
6
6
|
export declare const isMobile: () => boolean;
|
|
7
|
+
export declare const sleep: (ms: number) => Promise<unknown>;
|