clear-react-router 1.4.5 → 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 +168 -193
- package/dist/provider/Provider.d.ts +3 -3
- package/dist/types/global.d.ts +9 -4
- package/dist/utils/utils.d.ts +2 -2
- 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, 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
|
|
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
|
-
nextItem
|
|
70
|
-
},
|
|
71
|
-
children
|
|
72
|
-
})
|
|
67
|
+
children
|
|
73
68
|
})
|
|
74
69
|
})
|
|
75
70
|
});
|
|
@@ -113,10 +108,10 @@ var parseClientRouteItem = (el, parentParams = [], parentPath = "") => {
|
|
|
113
108
|
}, ...el.children?.flatMap((child) => parseClientRouteItem(child, currentParams, path)) || []];
|
|
114
109
|
};
|
|
115
110
|
var createRouter = (clientList) => clientList.flatMap((el) => parseClientRouteItem(el, []));
|
|
116
|
-
var getParamsObject = ({
|
|
117
|
-
if (!
|
|
111
|
+
var getParamsObject = ({ params, pathname }) => {
|
|
112
|
+
if (!params) return {};
|
|
118
113
|
const split = pathname.split("/");
|
|
119
|
-
return (
|
|
114
|
+
return (params || []).map((el) => ({
|
|
120
115
|
index: split.findIndex((item) => item === el.key),
|
|
121
116
|
value: el.value
|
|
122
117
|
})).reduce((acc, cur) => ({
|
|
@@ -136,71 +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
|
-
|
|
197
|
+
loaderState.current = {};
|
|
198
|
+
const nextItem = routeList.find((el) => el.path === ALL_LOCATIONS || comparePaths(el, nextLocation.pathname));
|
|
166
199
|
const params = getParamsObject({
|
|
167
|
-
|
|
200
|
+
params: nextItem?.params,
|
|
168
201
|
pathname: nextLocation.pathname
|
|
169
202
|
});
|
|
170
203
|
if (nextItem?.beforeLoad) try {
|
|
171
|
-
const redirect = async (location) => typeof location === "string" ?
|
|
204
|
+
const redirect = async (location) => await navigationHandler(typeof location === "string" ? { pathname: location } : location);
|
|
172
205
|
await nextItem.beforeLoad({
|
|
173
206
|
context,
|
|
174
207
|
redirect,
|
|
175
208
|
params,
|
|
176
209
|
setContext
|
|
177
210
|
});
|
|
178
|
-
|
|
179
|
-
...
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
beforeLoadError: null
|
|
183
|
-
}
|
|
184
|
-
}));
|
|
211
|
+
loaderState.current = {
|
|
212
|
+
...loaderState.current,
|
|
213
|
+
beforeLoadError: null
|
|
214
|
+
};
|
|
185
215
|
} catch (error) {
|
|
186
|
-
|
|
187
|
-
...
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
}));
|
|
193
|
-
transitionedNavigation(nextLocation);
|
|
194
|
-
return;
|
|
216
|
+
loaderState.current = {
|
|
217
|
+
...loaderState.current,
|
|
218
|
+
beforeLoadError: error
|
|
219
|
+
};
|
|
220
|
+
return transitionedNavigation(nextLocation, nextItem);
|
|
195
221
|
}
|
|
196
222
|
if (seq !== navigationSeq.current) return;
|
|
223
|
+
setCurrentLoaderFallback(nextItem?.loaderFallback);
|
|
197
224
|
await revalidateCache({
|
|
198
225
|
routeItem: nextItem,
|
|
199
|
-
|
|
226
|
+
loaderState,
|
|
200
227
|
pathname: nextLocation.pathname
|
|
201
228
|
});
|
|
202
229
|
if (seq !== navigationSeq.current) return;
|
|
203
|
-
transitionedNavigation(nextLocation);
|
|
230
|
+
transitionedNavigation(nextLocation, nextItem);
|
|
204
231
|
if (nextItem?.afterLoad) await nextItem.afterLoad({
|
|
205
232
|
context,
|
|
206
233
|
params,
|
|
@@ -211,8 +238,7 @@ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, s
|
|
|
211
238
|
revalidateCache,
|
|
212
239
|
routeList,
|
|
213
240
|
transitionedNavigation,
|
|
214
|
-
setContext
|
|
215
|
-
setLoaderState
|
|
241
|
+
setContext
|
|
216
242
|
]);
|
|
217
243
|
const setNextLocationRef = useLatest(navigationHandler);
|
|
218
244
|
const updateBlockedRoute = useCallback(({ type, payload = "" }) => setBlockedRoute((prevState) => {
|
|
@@ -266,14 +292,18 @@ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, s
|
|
|
266
292
|
}, [blockedRoute]),
|
|
267
293
|
updateLocation,
|
|
268
294
|
updateBlockedRoute,
|
|
269
|
-
|
|
295
|
+
routeItemData,
|
|
296
|
+
setSearch,
|
|
297
|
+
restoreScroll,
|
|
298
|
+
currentLoaderFallback
|
|
270
299
|
};
|
|
271
300
|
};
|
|
272
301
|
//#endregion
|
|
273
302
|
//#region hooks/useLoader.ts
|
|
274
|
-
var useLoader = ({ routeList, context, setContext
|
|
303
|
+
var useLoader = ({ routeList, context, setContext }) => {
|
|
275
304
|
const [isLoading, setIsLoading] = useState(false);
|
|
276
305
|
const cacheTimestampsRef = useRef({});
|
|
306
|
+
const loaderCacheRef = useRef({});
|
|
277
307
|
const isCacheItemFresh = useCallback(({ routeItem, pathname }) => {
|
|
278
308
|
if (!routeItem) return true;
|
|
279
309
|
const currentCacheTimestamp = cacheTimestampsRef.current[pathname];
|
|
@@ -281,16 +311,22 @@ var useLoader = ({ routeList, context, setContext, setLoaderState }) => {
|
|
|
281
311
|
if (!routeItem.staleTime) return true;
|
|
282
312
|
return Date.now() - currentCacheTimestamp < routeItem.staleTime;
|
|
283
313
|
}, []);
|
|
284
|
-
const revalidateCache = useCallback(async ({ routeItem,
|
|
285
|
-
if (!routeItem?.loader)
|
|
314
|
+
const revalidateCache = useCallback(async ({ routeItem, loaderState, pathname }) => {
|
|
315
|
+
if (!routeItem?.loader) {
|
|
316
|
+
if (loaderState) loaderState.current = {};
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
286
319
|
if (isCacheItemFresh({
|
|
287
320
|
routeItem,
|
|
288
321
|
pathname
|
|
289
|
-
}))
|
|
290
|
-
|
|
322
|
+
})) {
|
|
323
|
+
if (loaderState) loaderState.current = loaderCacheRef.current[pathname];
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (loaderState) setIsLoading(true);
|
|
291
327
|
try {
|
|
292
328
|
const params = getParamsObject({
|
|
293
|
-
routeItem,
|
|
329
|
+
params: routeItem.params,
|
|
294
330
|
pathname
|
|
295
331
|
});
|
|
296
332
|
const result = await routeItem?.loader({
|
|
@@ -302,31 +338,27 @@ var useLoader = ({ routeList, context, setContext, setLoaderState }) => {
|
|
|
302
338
|
...cacheTimestampsRef.current,
|
|
303
339
|
[pathname]: Date.now()
|
|
304
340
|
};
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
+
};
|
|
313
351
|
} catch (error) {
|
|
314
|
-
if (
|
|
315
|
-
...
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
loaderError: error
|
|
320
|
-
}
|
|
321
|
-
}));
|
|
322
|
-
} finally {
|
|
323
|
-
setTimeout(() => setIsLoading(false), 10);
|
|
352
|
+
if (loaderState) loaderState.current = {
|
|
353
|
+
...loaderState?.current,
|
|
354
|
+
data: null,
|
|
355
|
+
loaderError: error
|
|
356
|
+
};
|
|
324
357
|
}
|
|
325
358
|
}, [
|
|
326
359
|
context,
|
|
327
360
|
isCacheItemFresh,
|
|
328
|
-
setContext
|
|
329
|
-
setLoaderState
|
|
361
|
+
setContext
|
|
330
362
|
]);
|
|
331
363
|
return {
|
|
332
364
|
prefetchLoader: useCallback(async (pathname) => {
|
|
@@ -337,54 +369,52 @@ var useLoader = ({ routeList, context, setContext, setLoaderState }) => {
|
|
|
337
369
|
});
|
|
338
370
|
}, [revalidateCache, routeList]),
|
|
339
371
|
revalidateCache,
|
|
340
|
-
isLoading
|
|
372
|
+
isLoading,
|
|
373
|
+
setIsLoading
|
|
341
374
|
};
|
|
342
375
|
};
|
|
343
376
|
//#endregion
|
|
344
377
|
//#region components/RouterProvider.tsx
|
|
345
378
|
var RouterProvider = ({ children, routeList, context: initialContext = {} }) => {
|
|
346
|
-
const [location, setLocation] = useState({});
|
|
347
379
|
const [context, setContext] = useState(initialContext);
|
|
348
|
-
const
|
|
349
|
-
const { prefetchLoader, revalidateCache, isLoading } = useLoader({
|
|
380
|
+
const { prefetchLoader, revalidateCache, isLoading, setIsLoading } = useLoader({
|
|
350
381
|
routeList,
|
|
351
382
|
context,
|
|
352
|
-
setContext
|
|
353
|
-
setLoaderState
|
|
383
|
+
setContext
|
|
354
384
|
});
|
|
355
|
-
const { blockerState, updateLocation, updateBlockedRoute,
|
|
356
|
-
setLocation,
|
|
385
|
+
const { blockerState, updateLocation, updateBlockedRoute, routeItemData, setSearch, restoreScroll, currentLoaderFallback } = useHandleNavigation({
|
|
357
386
|
routeList,
|
|
358
387
|
context,
|
|
359
388
|
setContext,
|
|
360
389
|
revalidateCache,
|
|
361
|
-
|
|
390
|
+
setIsLoading
|
|
362
391
|
});
|
|
363
392
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Provider, {
|
|
364
393
|
...useMemo(() => ({
|
|
365
|
-
|
|
366
|
-
setLocation,
|
|
394
|
+
setSearch,
|
|
367
395
|
updateLocation,
|
|
368
|
-
loaderState,
|
|
369
396
|
prefetchLoader,
|
|
370
397
|
updateBlockedRoute,
|
|
371
398
|
blockerState,
|
|
372
399
|
context,
|
|
373
400
|
setContext,
|
|
374
401
|
routeList,
|
|
375
|
-
|
|
402
|
+
routeItemData,
|
|
403
|
+
restoreScroll,
|
|
404
|
+
currentLoaderFallback,
|
|
376
405
|
isLoading
|
|
377
406
|
}), [
|
|
378
407
|
blockerState,
|
|
379
408
|
context,
|
|
380
409
|
isLoading,
|
|
381
|
-
loaderState,
|
|
382
|
-
location,
|
|
383
410
|
prefetchLoader,
|
|
411
|
+
routeItemData,
|
|
384
412
|
routeList,
|
|
413
|
+
setSearch,
|
|
385
414
|
updateBlockedRoute,
|
|
386
415
|
updateLocation,
|
|
387
|
-
|
|
416
|
+
currentLoaderFallback,
|
|
417
|
+
restoreScroll
|
|
388
418
|
]),
|
|
389
419
|
children
|
|
390
420
|
});
|
|
@@ -399,7 +429,6 @@ var useServiceState = (reactContext) => {
|
|
|
399
429
|
var useNavigationState = () => useServiceState(NavigationContext);
|
|
400
430
|
var useRouterActions = () => useServiceState(ActionsContext);
|
|
401
431
|
var useRouterData = () => useServiceState(DataContext);
|
|
402
|
-
var usePropsData = () => useServiceState(PropsContext);
|
|
403
432
|
//#endregion
|
|
404
433
|
//#region hooks/useApplyCustomAnimation.ts
|
|
405
434
|
var useApplyCustomAnimation = (animationDuration) => {
|
|
@@ -439,44 +468,18 @@ var useApplyCustomAnimation = (animationDuration) => {
|
|
|
439
468
|
};
|
|
440
469
|
//#endregion
|
|
441
470
|
//#region hooks/usePreserveScroll.ts
|
|
442
|
-
var usePreserveScroll = (
|
|
443
|
-
const
|
|
444
|
-
const
|
|
445
|
-
useEffect(() => {
|
|
446
|
-
setScrollMap((prevState) => {
|
|
447
|
-
const scrollPosition = document.scrollingElement?.scrollTop ?? 0;
|
|
448
|
-
if (!scrollPosition || prevState[prevPathname.current] === scrollPosition) return prevState;
|
|
449
|
-
return {
|
|
450
|
-
...prevState,
|
|
451
|
-
[prevPathname.current]: scrollPosition
|
|
452
|
-
};
|
|
453
|
-
});
|
|
454
|
-
prevPathname.current = pathname;
|
|
455
|
-
}, [pathname, nextPathname]);
|
|
471
|
+
var usePreserveScroll = (preserveScroll) => {
|
|
472
|
+
const { restoreScroll } = useRouterActions();
|
|
473
|
+
const { routeItemData: { location: { pathname } } } = useNavigationState();
|
|
456
474
|
useEffect(() => {
|
|
457
|
-
if (
|
|
458
|
-
document.scrollingElement?.scrollTo({
|
|
459
|
-
top: scrollMap[pathname],
|
|
460
|
-
behavior: "smooth"
|
|
461
|
-
});
|
|
475
|
+
if (preserveScroll) restoreScroll();
|
|
462
476
|
}, [
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
477
|
+
preserveScroll,
|
|
478
|
+
restoreScroll,
|
|
479
|
+
pathname
|
|
466
480
|
]);
|
|
467
481
|
};
|
|
468
482
|
//#endregion
|
|
469
|
-
//#region context/RouterViewContext.ts
|
|
470
|
-
var RouterViewContext = createContext({});
|
|
471
|
-
//#endregion
|
|
472
|
-
//#region provider/ViewProvider.tsx
|
|
473
|
-
var ViewProvider = ({ children, params }) => {
|
|
474
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterViewContext.Provider, {
|
|
475
|
-
value: params,
|
|
476
|
-
children
|
|
477
|
-
});
|
|
478
|
-
};
|
|
479
|
-
//#endregion
|
|
480
483
|
//#region components/Spinner.tsx
|
|
481
484
|
var Spinner = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cr-spinner" });
|
|
482
485
|
//#endregion
|
|
@@ -487,57 +490,27 @@ var renderElement = (Component) => {
|
|
|
487
490
|
};
|
|
488
491
|
//#endregion
|
|
489
492
|
//#region components/Router.tsx
|
|
490
|
-
var
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const { routeList } = usePropsData();
|
|
494
|
-
const { loaderState } = useRouterData();
|
|
495
|
-
usePreserveScroll({
|
|
496
|
-
nextPathname: nextItem?.path,
|
|
497
|
-
pathname: location.pathname,
|
|
498
|
-
preserveScroll
|
|
499
|
-
});
|
|
493
|
+
var Router = ({ isAnimated, animationDuration, spinner = true, preserveScroll = true, showFallbackIfAnimated = false }) => {
|
|
494
|
+
const { isLoading, routeItemData: { routeItem, loaderState }, currentLoaderFallback } = useNavigationState();
|
|
495
|
+
usePreserveScroll(preserveScroll);
|
|
500
496
|
useApplyCustomAnimation(animationDuration);
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
});
|
|
510
|
-
}, [
|
|
511
|
-
location.pathname,
|
|
512
|
-
routeItem,
|
|
513
|
-
nextItem
|
|
514
|
-
]);
|
|
515
|
-
const shouldErrorElementShown = useMemo(() => Boolean(loaderState[location.pathname]?.loaderError || loaderState[location.pathname]?.beforeLoadError), [loaderState, location.pathname]);
|
|
516
|
-
if (!routeItem && !nextItem) return null;
|
|
517
|
-
if (!routeItem && nextItem) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ViewProvider, {
|
|
518
|
-
params,
|
|
519
|
-
children: renderElement(nextItem?.loaderFallback)
|
|
520
|
-
});
|
|
521
|
-
if (!isAnimated && !shouldErrorElementShown && isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ViewProvider, {
|
|
522
|
-
params,
|
|
523
|
-
children: renderElement(nextItem?.loaderFallback)
|
|
524
|
-
});
|
|
525
|
-
if (shouldErrorElementShown) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ViewProvider, {
|
|
526
|
-
params,
|
|
527
|
-
children: [renderElement(routeItem?.errorElement), spinner && isAnimated && isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})]
|
|
528
|
-
});
|
|
529
|
-
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", {
|
|
530
505
|
style: { viewTransitionName: "page" },
|
|
531
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.
|
|
532
|
-
params,
|
|
533
|
-
children: [renderElement(routeItem?.element) || null, spinner && isAnimated && isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})]
|
|
534
|
-
})
|
|
506
|
+
children: [renderElement(routeItem.element) || null, showSpinner && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})]
|
|
535
507
|
});
|
|
536
508
|
};
|
|
537
509
|
//#endregion
|
|
538
510
|
//#region hooks/useLocation.ts
|
|
539
511
|
var useLocation = () => {
|
|
540
|
-
|
|
512
|
+
const { routeItemData: { location } } = useNavigationState();
|
|
513
|
+
return location;
|
|
541
514
|
};
|
|
542
515
|
//#endregion
|
|
543
516
|
//#region hooks/useNavigate.ts
|
|
@@ -584,19 +557,24 @@ var Link = ({ children, to, prefetch = true, prefetchDelay = STANDARD_DELAY }) =
|
|
|
584
557
|
//#endregion
|
|
585
558
|
//#region hooks/useParams.ts
|
|
586
559
|
var useParams = () => {
|
|
587
|
-
|
|
560
|
+
const { routeItemData: { routeItem } } = useNavigationState();
|
|
561
|
+
if (!routeItem) return void 0;
|
|
562
|
+
return getParamsObject({
|
|
563
|
+
params: routeItem?.params,
|
|
564
|
+
pathname: location.pathname
|
|
565
|
+
});
|
|
588
566
|
};
|
|
589
567
|
//#endregion
|
|
590
568
|
//#region hooks/useLoaderState.ts
|
|
591
569
|
var useLoaderState = () => {
|
|
592
|
-
const {
|
|
593
|
-
|
|
594
|
-
return loaderState[pathname];
|
|
570
|
+
const { routeItemData: { loaderState } } = useNavigationState();
|
|
571
|
+
return loaderState;
|
|
595
572
|
};
|
|
596
573
|
//#endregion
|
|
597
574
|
//#region hooks/useBlocker.ts
|
|
598
575
|
var useBlocker = (blockerFn) => {
|
|
599
|
-
const {
|
|
576
|
+
const { blockerState } = useNavigationState();
|
|
577
|
+
const { routeItemData: { location: { pathname } } } = useNavigationState();
|
|
600
578
|
const { updateBlockedRoute } = useRouterActions();
|
|
601
579
|
const shouldBlock = blockerFn();
|
|
602
580
|
useEffect(() => updateBlockedRoute(shouldBlock ? {
|
|
@@ -639,8 +617,8 @@ var useRouterContext = () => {
|
|
|
639
617
|
//#endregion
|
|
640
618
|
//#region hooks/useSearchParams.ts
|
|
641
619
|
var useSearchParams = () => {
|
|
642
|
-
const location =
|
|
643
|
-
const {
|
|
620
|
+
const { routeItemData: { location } } = useNavigationState();
|
|
621
|
+
const { setSearch } = useRouterActions();
|
|
644
622
|
const locationRef = useLatest(location);
|
|
645
623
|
const searchString = useMemo(() => location.search ? location.search.replace("?", "") : location.pathname.split("?")?.[1] ?? "", [location.pathname, location.search]);
|
|
646
624
|
const searchParams = useMemo(() => new URLSearchParams(searchString), [searchString]);
|
|
@@ -652,12 +630,9 @@ var useSearchParams = () => {
|
|
|
652
630
|
const newSearch = params.toString();
|
|
653
631
|
const { pathname } = locationRef.current;
|
|
654
632
|
const search = newSearch ? `?${newSearch}` : "";
|
|
655
|
-
|
|
656
|
-
...prevState,
|
|
657
|
-
search
|
|
658
|
-
}));
|
|
633
|
+
setSearch(search);
|
|
659
634
|
history.replaceState(null, "", pathname + search);
|
|
660
|
-
}, [locationRef,
|
|
635
|
+
}, [locationRef, setSearch]);
|
|
661
636
|
return {
|
|
662
637
|
searchParams,
|
|
663
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,14 +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 RouteItemData = {
|
|
63
|
+
location: Location;
|
|
64
|
+
routeItem: RouteItem | undefined;
|
|
65
|
+
loaderState: LoaderState;
|
|
66
|
+
};
|
package/dist/utils/utils.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ClientRouteItem, Location, RouteItem } from '../types/global';
|
|
2
2
|
export declare const createRouter: (clientList: ClientRouteItem[]) => RouteItem[];
|
|
3
|
-
export declare const getParamsObject: ({
|
|
4
|
-
|
|
3
|
+
export declare const getParamsObject: ({ params, pathname }: {
|
|
4
|
+
params?: RouteItem["params"];
|
|
5
5
|
pathname: string;
|
|
6
6
|
}) => {};
|
|
7
7
|
export declare const parseWindowLocation: (location: typeof window.location) => Location;
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const RouterViewContext: import("react").Context<Record<string, string>>;
|