clear-react-router 1.4.6 → 1.4.7
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 -10
- package/dist/hooks/useHandleNavigation.d.ts +7 -5
- package/dist/hooks/useLoader.d.ts +4 -4
- package/dist/hooks/usePreserveScroll.d.ts +1 -7
- package/dist/hooks/useServiceContext.d.ts +0 -1
- package/dist/index.js +163 -192
- 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,21 @@
|
|
|
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
4
|
isLoading: boolean;
|
|
10
|
-
|
|
5
|
+
routeItemData: RouteItemData;
|
|
6
|
+
currentLoaderFallback: RouteItem['loaderFallback'];
|
|
11
7
|
};
|
|
12
8
|
export type ActionsContextValue = {
|
|
13
|
-
|
|
9
|
+
setSearch(arg: string): void;
|
|
14
10
|
updateLocation(route: Location): Promise<void>;
|
|
15
11
|
updateBlockedRoute(arg: UpdateBlockedRouteProps): void;
|
|
16
12
|
prefetchLoader(arg: string): Promise<void>;
|
|
17
13
|
setContext(arg: object): void;
|
|
14
|
+
restoreScroll(): void;
|
|
18
15
|
};
|
|
19
16
|
export type DataContextValue = {
|
|
20
|
-
loaderState: LoaderState;
|
|
21
17
|
context: Record<string, unknown>;
|
|
22
18
|
};
|
|
23
|
-
export declare const PropsContext: import("react").Context<PropsContextValue>;
|
|
24
19
|
export declare const ActionsContext: import("react").Context<ActionsContextValue>;
|
|
25
20
|
export declare const DataContext: import("react").Context<DataContextValue>;
|
|
26
21
|
export declare const NavigationContext: import("react").Context<NavigationContextValue>;
|
|
@@ -1,17 +1,19 @@
|
|
|
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
|
+
setIsLoading: Dispatch<SetStateAction<boolean>>;
|
|
10
9
|
};
|
|
11
|
-
export declare const useHandleNavigation: ({
|
|
10
|
+
export declare const useHandleNavigation: ({ routeList, context, revalidateCache, setContext, setIsLoading, }: UseHandleNavigation) => {
|
|
12
11
|
blockerState: BlockerState;
|
|
13
12
|
updateLocation: (nextLocation: Location) => Promise<void>;
|
|
14
13
|
updateBlockedRoute: ({ type, payload }: UpdateBlockedRouteProps) => void;
|
|
15
|
-
|
|
14
|
+
routeItemData: RouteItemData;
|
|
15
|
+
setSearch: (search: string) => void;
|
|
16
|
+
restoreScroll: () => void;
|
|
17
|
+
currentLoaderFallback: import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | (() => import("react").ReactElement) | undefined;
|
|
16
18
|
};
|
|
17
19
|
export {};
|
|
@@ -1,14 +1,14 @@
|
|
|
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,
|
|
10
|
+
revalidateCache: ({ routeItem, loaderState, pathname }: RevalidateCacheArgs) => Promise<void>;
|
|
12
11
|
isLoading: boolean;
|
|
12
|
+
setIsLoading: Dispatch<SetStateAction<boolean>>;
|
|
13
13
|
};
|
|
14
14
|
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,26 @@ 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, isLoading, 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
|
+
isLoading,
|
|
64
|
+
routeItemData,
|
|
65
|
+
currentLoaderFallback
|
|
63
66
|
},
|
|
64
|
-
children
|
|
65
|
-
value: {
|
|
66
|
-
blockerState,
|
|
67
|
-
location,
|
|
68
|
-
isLoading,
|
|
69
|
-
nextItemData
|
|
70
|
-
},
|
|
71
|
-
children
|
|
72
|
-
})
|
|
67
|
+
children
|
|
73
68
|
})
|
|
74
69
|
})
|
|
75
70
|
});
|
|
@@ -136,75 +131,103 @@ var comparePaths = (el, pathname) => {
|
|
|
136
131
|
};
|
|
137
132
|
//#endregion
|
|
138
133
|
//#region hooks/useHandleNavigation.ts
|
|
139
|
-
var
|
|
134
|
+
var ALL_LOCATIONS = "*";
|
|
135
|
+
var useHandleNavigation = ({ routeList, context, revalidateCache, setContext, setIsLoading }) => {
|
|
140
136
|
const [blockedRoute, setBlockedRoute] = useState({
|
|
141
137
|
from: "",
|
|
142
138
|
to: ""
|
|
143
139
|
});
|
|
144
|
-
const [
|
|
140
|
+
const [routeItemData, setRouteItemData] = useState({
|
|
141
|
+
location: {},
|
|
142
|
+
routeItem: void 0,
|
|
143
|
+
loaderState: {}
|
|
144
|
+
});
|
|
145
|
+
const [scrollMap, setScrollMap] = useState({});
|
|
146
|
+
const [currentLoaderFallback, setCurrentLoaderFallback] = useState();
|
|
147
|
+
const loaderState = useRef({});
|
|
145
148
|
const prevPathname = useRef("");
|
|
146
149
|
const navigationSeq = useRef(0);
|
|
147
|
-
const
|
|
148
|
-
|
|
150
|
+
const scrollMapRef = useLatest(scrollMap);
|
|
151
|
+
const setSearch = useCallback((search) => setRouteItemData((prevState) => ({
|
|
152
|
+
...prevState,
|
|
153
|
+
location: {
|
|
154
|
+
...prevState.location,
|
|
155
|
+
search
|
|
156
|
+
}
|
|
157
|
+
})), []);
|
|
158
|
+
const restoreScroll = useCallback(() => {
|
|
159
|
+
if (!prevPathname.current || !scrollMapRef.current[prevPathname.current]) return;
|
|
160
|
+
requestAnimationFrame(() => {
|
|
161
|
+
window.scrollTo({
|
|
162
|
+
top: scrollMapRef.current[prevPathname.current],
|
|
163
|
+
behavior: "smooth"
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}, [scrollMapRef]);
|
|
167
|
+
const navigation = useCallback((nextLocation, routeItem) => {
|
|
168
|
+
setRouteItemData({
|
|
169
|
+
location: nextLocation,
|
|
170
|
+
routeItem,
|
|
171
|
+
loaderState: loaderState.current
|
|
172
|
+
});
|
|
173
|
+
setIsLoading(false);
|
|
149
174
|
prevPathname.current = nextLocation.pathname;
|
|
150
175
|
const fullPath = nextLocation.search ? `${nextLocation.pathname}${nextLocation.search}` : nextLocation.pathname;
|
|
151
176
|
if (fullPath === window.location.pathname + window.location.search) return;
|
|
152
177
|
history.pushState(null, "", fullPath);
|
|
153
|
-
}, [
|
|
154
|
-
const transitionedNavigation = useCallback((nextLocation) => {
|
|
178
|
+
}, [setIsLoading]);
|
|
179
|
+
const transitionedNavigation = useCallback((nextLocation, routeItem) => {
|
|
180
|
+
setScrollMap((prevState) => {
|
|
181
|
+
const scrollPosition = document.scrollingElement?.scrollTop ?? 0;
|
|
182
|
+
if (!scrollPosition || prevState[prevPathname.current] === scrollPosition) return prevState;
|
|
183
|
+
return {
|
|
184
|
+
...prevState,
|
|
185
|
+
[prevPathname.current]: scrollPosition
|
|
186
|
+
};
|
|
187
|
+
});
|
|
155
188
|
try {
|
|
156
|
-
document.startViewTransition(() => navigation(nextLocation));
|
|
189
|
+
document.startViewTransition(() => navigation(nextLocation, routeItem));
|
|
157
190
|
} catch {
|
|
158
|
-
navigation(nextLocation);
|
|
191
|
+
navigation(nextLocation, routeItem);
|
|
159
192
|
}
|
|
160
193
|
}, [navigation]);
|
|
161
194
|
const navigationHandler = useCallback(async (nextLocation) => {
|
|
162
195
|
navigationSeq.current = navigationSeq.current + 1;
|
|
163
196
|
const seq = navigationSeq.current;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
loaderFallback: nextItem?.loaderFallback,
|
|
167
|
-
params: nextItem?.params,
|
|
168
|
-
pathname: nextLocation.pathname
|
|
169
|
-
});
|
|
197
|
+
loaderState.current = {};
|
|
198
|
+
const nextItem = routeList.find((el) => el.path === ALL_LOCATIONS || comparePaths(el, nextLocation.pathname));
|
|
170
199
|
const params = getParamsObject({
|
|
171
200
|
params: nextItem?.params,
|
|
172
201
|
pathname: nextLocation.pathname
|
|
173
202
|
});
|
|
174
203
|
if (nextItem?.beforeLoad) try {
|
|
175
|
-
const redirect = async (location) => typeof location === "string" ?
|
|
204
|
+
const redirect = async (location) => await navigationHandler(typeof location === "string" ? { pathname: location } : location);
|
|
176
205
|
await nextItem.beforeLoad({
|
|
177
206
|
context,
|
|
178
207
|
redirect,
|
|
179
208
|
params,
|
|
180
209
|
setContext
|
|
181
210
|
});
|
|
182
|
-
|
|
183
|
-
...
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
beforeLoadError: null
|
|
187
|
-
}
|
|
188
|
-
}));
|
|
211
|
+
loaderState.current = {
|
|
212
|
+
...loaderState.current,
|
|
213
|
+
beforeLoadError: null
|
|
214
|
+
};
|
|
189
215
|
} catch (error) {
|
|
190
|
-
|
|
191
|
-
...
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
}));
|
|
197
|
-
transitionedNavigation(nextLocation);
|
|
198
|
-
return;
|
|
216
|
+
loaderState.current = {
|
|
217
|
+
...loaderState.current,
|
|
218
|
+
beforeLoadError: error
|
|
219
|
+
};
|
|
220
|
+
return transitionedNavigation(nextLocation, nextItem);
|
|
199
221
|
}
|
|
200
222
|
if (seq !== navigationSeq.current) return;
|
|
223
|
+
setCurrentLoaderFallback(nextItem?.loaderFallback);
|
|
201
224
|
await revalidateCache({
|
|
202
225
|
routeItem: nextItem,
|
|
203
|
-
|
|
226
|
+
loaderState,
|
|
204
227
|
pathname: nextLocation.pathname
|
|
205
228
|
});
|
|
206
229
|
if (seq !== navigationSeq.current) return;
|
|
207
|
-
transitionedNavigation(nextLocation);
|
|
230
|
+
transitionedNavigation(nextLocation, nextItem);
|
|
208
231
|
if (nextItem?.afterLoad) await nextItem.afterLoad({
|
|
209
232
|
context,
|
|
210
233
|
params,
|
|
@@ -215,8 +238,7 @@ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, s
|
|
|
215
238
|
revalidateCache,
|
|
216
239
|
routeList,
|
|
217
240
|
transitionedNavigation,
|
|
218
|
-
setContext
|
|
219
|
-
setLoaderState
|
|
241
|
+
setContext
|
|
220
242
|
]);
|
|
221
243
|
const setNextLocationRef = useLatest(navigationHandler);
|
|
222
244
|
const updateBlockedRoute = useCallback(({ type, payload = "" }) => setBlockedRoute((prevState) => {
|
|
@@ -270,14 +292,18 @@ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, s
|
|
|
270
292
|
}, [blockedRoute]),
|
|
271
293
|
updateLocation,
|
|
272
294
|
updateBlockedRoute,
|
|
273
|
-
|
|
295
|
+
routeItemData,
|
|
296
|
+
setSearch,
|
|
297
|
+
restoreScroll,
|
|
298
|
+
currentLoaderFallback
|
|
274
299
|
};
|
|
275
300
|
};
|
|
276
301
|
//#endregion
|
|
277
302
|
//#region hooks/useLoader.ts
|
|
278
|
-
var useLoader = ({ routeList, context, setContext
|
|
303
|
+
var useLoader = ({ routeList, context, setContext }) => {
|
|
279
304
|
const [isLoading, setIsLoading] = useState(false);
|
|
280
305
|
const cacheTimestampsRef = useRef({});
|
|
306
|
+
const loaderCacheRef = useRef({});
|
|
281
307
|
const isCacheItemFresh = useCallback(({ routeItem, pathname }) => {
|
|
282
308
|
if (!routeItem) return true;
|
|
283
309
|
const currentCacheTimestamp = cacheTimestampsRef.current[pathname];
|
|
@@ -285,13 +311,19 @@ var useLoader = ({ routeList, context, setContext, setLoaderState }) => {
|
|
|
285
311
|
if (!routeItem.staleTime) return true;
|
|
286
312
|
return Date.now() - currentCacheTimestamp < routeItem.staleTime;
|
|
287
313
|
}, []);
|
|
288
|
-
const revalidateCache = useCallback(async ({ routeItem,
|
|
289
|
-
if (!routeItem?.loader)
|
|
314
|
+
const revalidateCache = useCallback(async ({ routeItem, loaderState, pathname }) => {
|
|
315
|
+
if (!routeItem?.loader) {
|
|
316
|
+
if (loaderState) loaderState.current = {};
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
290
319
|
if (isCacheItemFresh({
|
|
291
320
|
routeItem,
|
|
292
321
|
pathname
|
|
293
|
-
}))
|
|
294
|
-
|
|
322
|
+
})) {
|
|
323
|
+
if (loaderState) loaderState.current = loaderCacheRef.current[pathname];
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (loaderState) setIsLoading(true);
|
|
295
327
|
try {
|
|
296
328
|
const params = getParamsObject({
|
|
297
329
|
params: routeItem.params,
|
|
@@ -306,31 +338,27 @@ var useLoader = ({ routeList, context, setContext, setLoaderState }) => {
|
|
|
306
338
|
...cacheTimestampsRef.current,
|
|
307
339
|
[pathname]: Date.now()
|
|
308
340
|
};
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
341
|
+
loaderCacheRef.current[pathname] = {
|
|
342
|
+
data: result,
|
|
343
|
+
loaderError: null,
|
|
344
|
+
beforeLoadError: null
|
|
345
|
+
};
|
|
346
|
+
if (loaderState) loaderState.current = {
|
|
347
|
+
...loaderState?.current,
|
|
348
|
+
data: result,
|
|
349
|
+
loaderError: null
|
|
350
|
+
};
|
|
317
351
|
} catch (error) {
|
|
318
|
-
if (
|
|
319
|
-
...
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
loaderError: error
|
|
324
|
-
}
|
|
325
|
-
}));
|
|
326
|
-
} finally {
|
|
327
|
-
setTimeout(() => setIsLoading(false), 10);
|
|
352
|
+
if (loaderState) loaderState.current = {
|
|
353
|
+
...loaderState?.current,
|
|
354
|
+
data: null,
|
|
355
|
+
loaderError: error
|
|
356
|
+
};
|
|
328
357
|
}
|
|
329
358
|
}, [
|
|
330
359
|
context,
|
|
331
360
|
isCacheItemFresh,
|
|
332
|
-
setContext
|
|
333
|
-
setLoaderState
|
|
361
|
+
setContext
|
|
334
362
|
]);
|
|
335
363
|
return {
|
|
336
364
|
prefetchLoader: useCallback(async (pathname) => {
|
|
@@ -341,54 +369,52 @@ var useLoader = ({ routeList, context, setContext, setLoaderState }) => {
|
|
|
341
369
|
});
|
|
342
370
|
}, [revalidateCache, routeList]),
|
|
343
371
|
revalidateCache,
|
|
344
|
-
isLoading
|
|
372
|
+
isLoading,
|
|
373
|
+
setIsLoading
|
|
345
374
|
};
|
|
346
375
|
};
|
|
347
376
|
//#endregion
|
|
348
377
|
//#region components/RouterProvider.tsx
|
|
349
378
|
var RouterProvider = ({ children, routeList, context: initialContext = {} }) => {
|
|
350
|
-
const [location, setLocation] = useState({});
|
|
351
379
|
const [context, setContext] = useState(initialContext);
|
|
352
|
-
const
|
|
353
|
-
const { prefetchLoader, revalidateCache, isLoading } = useLoader({
|
|
380
|
+
const { prefetchLoader, revalidateCache, isLoading, setIsLoading } = useLoader({
|
|
354
381
|
routeList,
|
|
355
382
|
context,
|
|
356
|
-
setContext
|
|
357
|
-
setLoaderState
|
|
383
|
+
setContext
|
|
358
384
|
});
|
|
359
|
-
const { blockerState, updateLocation, updateBlockedRoute,
|
|
360
|
-
setLocation,
|
|
385
|
+
const { blockerState, updateLocation, updateBlockedRoute, routeItemData, setSearch, restoreScroll, currentLoaderFallback } = useHandleNavigation({
|
|
361
386
|
routeList,
|
|
362
387
|
context,
|
|
363
388
|
setContext,
|
|
364
389
|
revalidateCache,
|
|
365
|
-
|
|
390
|
+
setIsLoading
|
|
366
391
|
});
|
|
367
392
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Provider, {
|
|
368
393
|
...useMemo(() => ({
|
|
369
|
-
|
|
370
|
-
setLocation,
|
|
394
|
+
setSearch,
|
|
371
395
|
updateLocation,
|
|
372
|
-
loaderState,
|
|
373
396
|
prefetchLoader,
|
|
374
397
|
updateBlockedRoute,
|
|
375
398
|
blockerState,
|
|
376
399
|
context,
|
|
377
400
|
setContext,
|
|
378
401
|
routeList,
|
|
379
|
-
|
|
402
|
+
routeItemData,
|
|
403
|
+
restoreScroll,
|
|
404
|
+
currentLoaderFallback,
|
|
380
405
|
isLoading
|
|
381
406
|
}), [
|
|
382
407
|
blockerState,
|
|
383
408
|
context,
|
|
384
409
|
isLoading,
|
|
385
|
-
loaderState,
|
|
386
|
-
location,
|
|
387
410
|
prefetchLoader,
|
|
411
|
+
routeItemData,
|
|
388
412
|
routeList,
|
|
413
|
+
setSearch,
|
|
389
414
|
updateBlockedRoute,
|
|
390
415
|
updateLocation,
|
|
391
|
-
|
|
416
|
+
currentLoaderFallback,
|
|
417
|
+
restoreScroll
|
|
392
418
|
]),
|
|
393
419
|
children
|
|
394
420
|
});
|
|
@@ -403,7 +429,6 @@ var useServiceState = (reactContext) => {
|
|
|
403
429
|
var useNavigationState = () => useServiceState(NavigationContext);
|
|
404
430
|
var useRouterActions = () => useServiceState(ActionsContext);
|
|
405
431
|
var useRouterData = () => useServiceState(DataContext);
|
|
406
|
-
var usePropsData = () => useServiceState(PropsContext);
|
|
407
432
|
//#endregion
|
|
408
433
|
//#region hooks/useApplyCustomAnimation.ts
|
|
409
434
|
var useApplyCustomAnimation = (animationDuration) => {
|
|
@@ -443,46 +468,18 @@ var useApplyCustomAnimation = (animationDuration) => {
|
|
|
443
468
|
};
|
|
444
469
|
//#endregion
|
|
445
470
|
//#region hooks/usePreserveScroll.ts
|
|
446
|
-
var usePreserveScroll = (
|
|
447
|
-
const
|
|
448
|
-
const
|
|
449
|
-
useEffect(() => {
|
|
450
|
-
setScrollMap((prevState) => {
|
|
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]);
|
|
471
|
+
var usePreserveScroll = (preserveScroll) => {
|
|
472
|
+
const { restoreScroll } = useRouterActions();
|
|
473
|
+
const { routeItemData: { location: { pathname } } } = useNavigationState();
|
|
460
474
|
useEffect(() => {
|
|
461
|
-
if (
|
|
462
|
-
requestAnimationFrame(() => {
|
|
463
|
-
window.scrollTo({
|
|
464
|
-
top: scrollMap[pathname],
|
|
465
|
-
behavior: "smooth"
|
|
466
|
-
});
|
|
467
|
-
});
|
|
475
|
+
if (preserveScroll) restoreScroll();
|
|
468
476
|
}, [
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
477
|
+
preserveScroll,
|
|
478
|
+
restoreScroll,
|
|
479
|
+
pathname
|
|
472
480
|
]);
|
|
473
481
|
};
|
|
474
482
|
//#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
483
|
//#region components/Spinner.tsx
|
|
487
484
|
var Spinner = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cr-spinner" });
|
|
488
485
|
//#endregion
|
|
@@ -493,55 +490,27 @@ var renderElement = (Component) => {
|
|
|
493
490
|
};
|
|
494
491
|
//#endregion
|
|
495
492
|
//#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
|
-
});
|
|
493
|
+
var Router = ({ isAnimated, animationDuration, spinner = true, preserveScroll = true, showFallbackIfAnimated = false }) => {
|
|
494
|
+
const { isLoading, routeItemData: { routeItem, loaderState }, currentLoaderFallback } = useNavigationState();
|
|
495
|
+
usePreserveScroll(preserveScroll);
|
|
506
496
|
useApplyCustomAnimation(animationDuration);
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
location.pathname,
|
|
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", {
|
|
497
|
+
const showErrorElement = useMemo(() => Boolean(loaderState.loaderError || loaderState.beforeLoadError), [loaderState]);
|
|
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, isLoading, 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>>;
|