clear-react-router 1.1.5 → 1.2.0
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 +92 -20
- package/dist/clear-react-router.css +23 -0
- package/dist/components/Router.d.ts +4 -2
- package/dist/components/Spinner/Spinner.d.ts +1 -0
- package/dist/hooks/useApplyCustomAnimation.d.ts +2 -0
- package/dist/hooks/useHandleNavigation.d.ts +2 -1
- package/dist/hooks/useLocation.d.ts +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +103 -79
- package/dist/types/global.d.ts +8 -1
- package/package.json +1 -1
- package/dist/utils/redirect.d.ts +0 -7
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ A lightweight, type-safe routing library for React applications with nested rout
|
|
|
7
7
|
- 🧩 **Nested Routes** - Organize your UI with nested layouts and routes
|
|
8
8
|
- ⚡ **Data Loading** - Built-in loaders with caching and stale-while-revalidate strategy
|
|
9
9
|
- 🔒 **Navigation Blocking** - Prevent accidental navigation with `useBlocker`
|
|
10
|
+
- ✨ **Smooth Animations** - Page transitions with fade and slide effects (customizable type and duration)
|
|
10
11
|
- 🎯 **Type-safe Redirects** - Redirect from loaders and beforeLoad hooks
|
|
11
12
|
- 📦 **Prefetching** - Preload data on hover for instant navigation
|
|
12
13
|
- 🚀 **Lazy Loading** - Code-split your routes with dynamic imports for optimal performance
|
|
@@ -25,7 +26,7 @@ Normalizes route configuration. Handles wildcard `*` routes, extracts dynamic pa
|
|
|
25
26
|
| `path` | `string` | Route path, e.g., `/user/:userId` |
|
|
26
27
|
| `element` | `ReactElement \| () => ReactElement \| LazyComponent` | Component to render |
|
|
27
28
|
| `loader` | `() => Promise<unknown>` | Fetch data |
|
|
28
|
-
| `beforeLoad` | `(context) => Promise<void>` | Auth checks
|
|
29
|
+
| `beforeLoad` | `({ context, redirect }) => Promise<void>` | Auth checks and redirects. `redirect` is provided by the router |
|
|
29
30
|
| `afterLoad` | `(context) => Promise<void>` | Analytics, side effects |
|
|
30
31
|
| `fallback` | `ReactElement \| () => ReactElement` | Loading fallback (for lazy loading) |
|
|
31
32
|
| `loaderFallback` | `ReactElement \| () => ReactElement` | Loading fallback (for loader) |
|
|
@@ -37,10 +38,21 @@ Normalizes route configuration. Handles wildcard `*` routes, extracts dynamic pa
|
|
|
37
38
|
|
|
38
39
|
Main component that renders the application based on current URL.
|
|
39
40
|
|
|
40
|
-
| Prop | Type | Description |
|
|
41
|
-
|
|
42
|
-
| `routeList` | `RouteItem[]` | Array of route configurations |
|
|
43
|
-
| `context` | `object` | Optional initial context (user, theme, etc.) |
|
|
41
|
+
| Prop | Type | Default | Description |
|
|
42
|
+
|------|------|---------|-------------|
|
|
43
|
+
| `routeList` | `RouteItem[]` | required | Array of route configurations |
|
|
44
|
+
| `context` | `object` | `{}` | Optional initial context (user, theme, etc.) |
|
|
45
|
+
| `isAnimated` | `boolean` | `false` | Enable smooth page transitions |
|
|
46
|
+
| `animationOptions` | `AnimationOptions` | `{ duration: 300, name: 'fade' }` | Animation settings (only when `isAnimated` is `true`) |
|
|
47
|
+
|
|
48
|
+
**`AnimationOptions`:**
|
|
49
|
+
|
|
50
|
+
| Property | Type | Default | Description |
|
|
51
|
+
|----------|------|---------|-------------|
|
|
52
|
+
| `duration` | `number` | `300` | Animation duration in milliseconds |
|
|
53
|
+
| `name` | `'fade' \| 'slide-left' \| 'slide-right'` | `'fade'` | Type of transition effect |
|
|
54
|
+
|
|
55
|
+
> **Note:** When `isAnimated` is enabled, the `loaderFallback` is not displayed. Instead, a small spinner appears in the corner while data loads, ensuring smooth transitions without layout shifts.
|
|
44
56
|
|
|
45
57
|
### `Link`
|
|
46
58
|
|
|
@@ -52,13 +64,26 @@ Component for client-side navigation with prefetch support.
|
|
|
52
64
|
| `prefetch` | `boolean` | `true` |
|
|
53
65
|
| `children` | `ReactElement` | required |
|
|
54
66
|
|
|
55
|
-
### `redirect
|
|
67
|
+
### `redirect`
|
|
68
|
+
|
|
69
|
+
Function provided to `beforeLoad` for programmatic redirection.
|
|
70
|
+
|
|
71
|
+
**Type:** `(arg: Location) => Promise<void>`
|
|
56
72
|
|
|
57
|
-
Redirects from `beforeLoad`.
|
|
58
73
|
```
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
74
|
+
import type { Location } from 'clear-react-router';
|
|
75
|
+
|
|
76
|
+
const routes = createRouter([
|
|
77
|
+
{
|
|
78
|
+
path: '/dashboard',
|
|
79
|
+
element: <Dashboard />,
|
|
80
|
+
beforeLoad: ({ context, redirect }) => {
|
|
81
|
+
if (!context.isAuthorized) {
|
|
82
|
+
return redirect({ pathname: '/' });
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
]);
|
|
62
87
|
```
|
|
63
88
|
|
|
64
89
|
## Hooks
|
|
@@ -131,14 +156,6 @@ useEffect(() => {
|
|
|
131
156
|
|
|
132
157
|
Executes a callback when the page is about to be closed or reloaded. Perfect for auto-saving data at the last moment.
|
|
133
158
|
|
|
134
|
-
### `useRouterContext()`
|
|
135
|
-
|
|
136
|
-
Handles router context.
|
|
137
|
-
```
|
|
138
|
-
const { setContext, context } = useRouterContext();
|
|
139
|
-
const loginHandler = () => setContext({ ...context, user: { name: 'John' } });
|
|
140
|
-
```
|
|
141
|
-
|
|
142
159
|
**Parameters:**
|
|
143
160
|
|
|
144
161
|
| Parameter | Type | Description |
|
|
@@ -157,6 +174,14 @@ const onSave = useCallback(() => {
|
|
|
157
174
|
useBeforeUnload(text ? onSave : undefined);
|
|
158
175
|
```
|
|
159
176
|
|
|
177
|
+
### `useRouterContext()`
|
|
178
|
+
|
|
179
|
+
Handles router context.
|
|
180
|
+
```
|
|
181
|
+
const { setContext, context } = useRouterContext();
|
|
182
|
+
const loginHandler = () => setContext({ ...context, user: { name: 'John' } });
|
|
183
|
+
```
|
|
184
|
+
|
|
160
185
|
## Lazy Loading
|
|
161
186
|
|
|
162
187
|
Clear Router supports code-splitting out of the box. Simply pass a function that returns a dynamic import:
|
|
@@ -167,10 +192,57 @@ Clear Router supports code-splitting out of the box. Simply pass a function that
|
|
|
167
192
|
fallback: () => <div>Loading...</div>,
|
|
168
193
|
}
|
|
169
194
|
```
|
|
195
|
+
## Animations
|
|
196
|
+
|
|
197
|
+
Clear Router supports smooth page transitions using the native View Transitions API. When animations are enabled, the router waits for all data to load before starting the transition, ensuring a jank-free experience.
|
|
198
|
+
|
|
199
|
+
### Quick Start
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
import { Router } from 'clear-react-router';
|
|
203
|
+
|
|
204
|
+
// Enable default fade animation
|
|
205
|
+
<Router routeList={routes} isAnimated />
|
|
206
|
+
|
|
207
|
+
// Custom animation
|
|
208
|
+
<Router
|
|
209
|
+
routeList={routes}
|
|
210
|
+
isAnimated
|
|
211
|
+
animationOptions={{
|
|
212
|
+
duration: 500, // milliseconds
|
|
213
|
+
name: 'slide-left' // 'fade' | 'slide-left' | 'slide-right'
|
|
214
|
+
}}
|
|
215
|
+
/>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## How It Works
|
|
219
|
+
|
|
220
|
+
- **Data loads first** — All `loader` and `beforeLoad` hooks complete before animation starts
|
|
221
|
+
- **No `loaderFallback`** — The `loaderFallback` is not shown during animated transitions
|
|
222
|
+
- **Subtle spinner** — A small spinner appears in the top-left corner while data is loading, so users know the app is responsive
|
|
223
|
+
- **Native API** — Uses `document.startViewTransition` for smooth, hardware-accelerated animations
|
|
224
|
+
|
|
225
|
+
## Animation Types
|
|
226
|
+
|
|
227
|
+
| Name | Effect |
|
|
228
|
+
|------|--------|
|
|
229
|
+
| `fade` | Cross-fade between pages (default) |
|
|
230
|
+
| `slide-left` | New page slides in from right, old page slides out to left |
|
|
231
|
+
| `slide-right` | New page slides in from left, old page slides out to right |
|
|
232
|
+
|
|
233
|
+
## Browser Support
|
|
234
|
+
|
|
235
|
+
View Transitions API requires modern browsers:
|
|
236
|
+
|
|
237
|
+
- Chrome/Edge 111+
|
|
238
|
+
- Safari 18+
|
|
239
|
+
- Firefox 144+
|
|
240
|
+
|
|
241
|
+
For older browsers, the router gracefully falls back to regular navigation without animation.
|
|
170
242
|
|
|
171
243
|
## Requirements
|
|
172
244
|
- React 16.6+ (for React.lazy and Suspense)
|
|
173
245
|
- Use `default` export for your lazy-loaded components
|
|
174
246
|
|
|
175
|
-
|
|
176
|
-
|
|
247
|
+
## License
|
|
248
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
._spinner_1tekw_1 {
|
|
2
|
+
position: fixed;
|
|
3
|
+
top: 5px;
|
|
4
|
+
left: 5px;
|
|
5
|
+
z-index: 9999;
|
|
6
|
+
width: 1rem;
|
|
7
|
+
height: 1rem;
|
|
8
|
+
border: 2px solid gray;
|
|
9
|
+
border-bottom-color: transparent;
|
|
10
|
+
border-radius: 50%;
|
|
11
|
+
display: inline-block;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
animation: _rotation_1tekw_1 1s linear infinite;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@keyframes _rotation_1tekw_1 {
|
|
17
|
+
0% {
|
|
18
|
+
transform: rotate(0deg);
|
|
19
|
+
}
|
|
20
|
+
100% {
|
|
21
|
+
transform: rotate(360deg);
|
|
22
|
+
}
|
|
23
|
+
}/*$vite$:1*/
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { AnimationOptions, RouteItem } from '../types/global';
|
|
2
2
|
type RouterProps = {
|
|
3
3
|
routeList: RouteItem[];
|
|
4
4
|
context?: Record<string, unknown>;
|
|
5
|
+
isAnimated?: boolean;
|
|
6
|
+
animationOptions?: AnimationOptions;
|
|
5
7
|
};
|
|
6
|
-
export declare const Router: ({ routeList, context: initialContext }: RouterProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare const Router: ({ routeList, context: initialContext, isAnimated, animationOptions, }: RouterProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
9
|
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const Spinner: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -4,8 +4,9 @@ type UseHandleNavigation = {
|
|
|
4
4
|
setLocation: (arg: Location) => void;
|
|
5
5
|
context: Record<string, unknown>;
|
|
6
6
|
revalidateCache(routeItem?: RouteItem, isCurrentRoute?: boolean): Promise<void>;
|
|
7
|
+
isAnimated: boolean;
|
|
7
8
|
};
|
|
8
|
-
export declare const useHandleNavigation: ({ setLocation, routeList, context, revalidateCache }: UseHandleNavigation) => {
|
|
9
|
+
export declare const useHandleNavigation: ({ setLocation, routeList, context, revalidateCache, isAnimated, }: UseHandleNavigation) => {
|
|
9
10
|
blockerState: BlockerState;
|
|
10
11
|
updateLocation: (nextLocation: Location) => Promise<void>;
|
|
11
12
|
updateBlockedRoute: ({ type, payload }: UpdateBlockedRouteProps) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const useLocation: () => import("
|
|
1
|
+
export declare const useLocation: () => import("..").Location;
|
package/dist/index.d.ts
CHANGED
|
@@ -8,5 +8,4 @@ export { useBlocker } from './hooks/useBlocker';
|
|
|
8
8
|
export { useBeforeUnload } from './hooks/useBeforeUnload';
|
|
9
9
|
export { useRouterContext } from './hooks/useRouterContext';
|
|
10
10
|
export { createRouter } from './utils/utils';
|
|
11
|
-
export {
|
|
12
|
-
export type { RouteItem, BlockerState } from './types/global';
|
|
11
|
+
export type { RouteItem, BlockerState, Location } from './types/global';
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ var require_react_jsx_runtime_production = /* @__PURE__ */ __commonJSMin(((expor
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
exports.jsx = jsxProd;
|
|
40
|
+
exports.jsxs = jsxProd;
|
|
40
41
|
}));
|
|
41
42
|
//#endregion
|
|
42
43
|
//#region provider/RouterProvider.tsx
|
|
@@ -129,37 +130,52 @@ var useLatest = (value) => {
|
|
|
129
130
|
};
|
|
130
131
|
//#endregion
|
|
131
132
|
//#region hooks/useHandleNavigation.ts
|
|
132
|
-
var
|
|
133
|
-
var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache }) => {
|
|
133
|
+
var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, isAnimated }) => {
|
|
134
134
|
const [blockedRoute, setBlockedRoute] = useState({
|
|
135
135
|
from: "",
|
|
136
136
|
to: ""
|
|
137
137
|
});
|
|
138
138
|
const prevPathname = useRef("");
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
history.replaceState(null, "", redirect.url);
|
|
152
|
-
setLocation({
|
|
153
|
-
pathname: redirect.url,
|
|
154
|
-
search: redirect.search
|
|
155
|
-
});
|
|
139
|
+
const navigationSeq = useRef(0);
|
|
140
|
+
const navigation = useCallback((nextLocation) => {
|
|
141
|
+
setLocation(nextLocation);
|
|
142
|
+
prevPathname.current = nextLocation.pathname;
|
|
143
|
+
if (nextLocation.pathname === window.location.pathname) return;
|
|
144
|
+
history.pushState(null, "", nextLocation.pathname);
|
|
145
|
+
}, [setLocation]);
|
|
146
|
+
const transitionedNavigation = useCallback(({ nextLocation, isFirstCall, isAnimated }) => {
|
|
147
|
+
if (isAnimated && !isFirstCall) try {
|
|
148
|
+
document.startViewTransition(() => navigation(nextLocation));
|
|
149
|
+
} catch {
|
|
150
|
+
navigation(nextLocation);
|
|
156
151
|
}
|
|
152
|
+
else navigation(nextLocation);
|
|
153
|
+
}, [navigation]);
|
|
154
|
+
const setNextLocation = useCallback(async (nextLocation, isFirstCall) => {
|
|
155
|
+
navigationSeq.current = navigationSeq.current + 1;
|
|
156
|
+
const seq = navigationSeq.current;
|
|
157
|
+
const nextItem = routeList.find((el) => comparePaths(el, nextLocation.pathname));
|
|
158
|
+
if (nextItem?.beforeLoad) await nextItem.beforeLoad({
|
|
159
|
+
context,
|
|
160
|
+
redirect: setNextLocation
|
|
161
|
+
});
|
|
162
|
+
if (seq !== navigationSeq.current) return;
|
|
163
|
+
await revalidateCache(nextItem, true);
|
|
164
|
+
if (seq !== navigationSeq.current) return;
|
|
165
|
+
transitionedNavigation({
|
|
166
|
+
nextLocation,
|
|
167
|
+
isAnimated,
|
|
168
|
+
isFirstCall
|
|
169
|
+
});
|
|
170
|
+
if (nextItem?.afterLoad) await nextItem.afterLoad(context);
|
|
157
171
|
}, [
|
|
158
172
|
context,
|
|
173
|
+
isAnimated,
|
|
159
174
|
revalidateCache,
|
|
160
175
|
routeList,
|
|
161
|
-
|
|
162
|
-
])
|
|
176
|
+
transitionedNavigation
|
|
177
|
+
]);
|
|
178
|
+
const setNextLocationRef = useLatest(setNextLocation);
|
|
163
179
|
const updateBlockedRoute = useCallback(({ type, payload = "" }) => setBlockedRoute((prevState) => {
|
|
164
180
|
if (prevState.from === payload && type === "charge") return prevState;
|
|
165
181
|
if (payload && prevState.from !== payload && type === "charge") return {
|
|
@@ -200,7 +216,7 @@ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache })
|
|
|
200
216
|
}, [blockedRoute.from, setNextLocationRef]);
|
|
201
217
|
useEffect(() => {
|
|
202
218
|
const currentLocation = parseWindowLocation(window.location);
|
|
203
|
-
setNextLocationRef.current(currentLocation);
|
|
219
|
+
setNextLocationRef.current(currentLocation, true);
|
|
204
220
|
prevPathname.current = currentLocation.pathname;
|
|
205
221
|
}, [setNextLocationRef]);
|
|
206
222
|
return {
|
|
@@ -265,6 +281,63 @@ var useLoader = (routeList) => {
|
|
|
265
281
|
};
|
|
266
282
|
};
|
|
267
283
|
//#endregion
|
|
284
|
+
//#region hooks/useApplyCustomAnimation.ts
|
|
285
|
+
var useApplyCustomAnimation = (animationOptions) => {
|
|
286
|
+
useEffect(() => {
|
|
287
|
+
if (!animationOptions?.duration) return;
|
|
288
|
+
const style = document.createElement("style");
|
|
289
|
+
style.id = "dynamic-view-transition-duration-style";
|
|
290
|
+
style.textContent = `::view-transition-group(root) { animation-duration: ${animationOptions.duration}ms; }`;
|
|
291
|
+
document.head.appendChild(style);
|
|
292
|
+
return () => style.remove();
|
|
293
|
+
}, [animationOptions]);
|
|
294
|
+
useEffect(() => {
|
|
295
|
+
if (!animationOptions.name) return;
|
|
296
|
+
const style = document.createElement("style");
|
|
297
|
+
style.id = "dynamic-view-transition-duration-name";
|
|
298
|
+
style.textContent = `
|
|
299
|
+
@keyframes fade-out {
|
|
300
|
+
from { opacity: 1; }
|
|
301
|
+
to { opacity: 0; }
|
|
302
|
+
}
|
|
303
|
+
@keyframes fade-in {
|
|
304
|
+
from { opacity: 0; }
|
|
305
|
+
to { opacity: 1; }
|
|
306
|
+
}
|
|
307
|
+
@keyframes slide-left-out {
|
|
308
|
+
from { transform: translateX(0); }
|
|
309
|
+
to { transform: translateX(-100%); }
|
|
310
|
+
}
|
|
311
|
+
@keyframes slide-left-in {
|
|
312
|
+
from { transform: translateX(100%); }
|
|
313
|
+
to { transform: translateX(0); }
|
|
314
|
+
}
|
|
315
|
+
@keyframes slide-right-out {
|
|
316
|
+
from { transform: translateX(0); }
|
|
317
|
+
to { transform: translateX(100%); }
|
|
318
|
+
}
|
|
319
|
+
@keyframes slide-right-in {
|
|
320
|
+
from { transform: translateX(-100%); }
|
|
321
|
+
to { transform: translateX(0); }
|
|
322
|
+
}
|
|
323
|
+
::view-transition-old(root) {
|
|
324
|
+
animation: ${animationOptions.name}-out ${animationOptions.duration ?? 800}ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
325
|
+
}
|
|
326
|
+
::view-transition-new(root) {
|
|
327
|
+
animation: ${animationOptions.name}-in ${animationOptions.duration ?? 800}ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
328
|
+
}`;
|
|
329
|
+
document.head.appendChild(style);
|
|
330
|
+
return () => style.remove();
|
|
331
|
+
}, [animationOptions]);
|
|
332
|
+
};
|
|
333
|
+
var Spinner_module_default = {
|
|
334
|
+
spinner: "_spinner_1tekw_1",
|
|
335
|
+
rotation: "_rotation_1tekw_1"
|
|
336
|
+
};
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region components/Spinner/Spinner.tsx
|
|
339
|
+
var Spinner = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: Spinner_module_default.spinner });
|
|
340
|
+
//#endregion
|
|
268
341
|
//#region utils/renderElement.tsx
|
|
269
342
|
var renderElement = (Component) => {
|
|
270
343
|
if (!Component) return null;
|
|
@@ -274,16 +347,18 @@ var renderElement = (Component) => {
|
|
|
274
347
|
//#region components/Router.tsx
|
|
275
348
|
var PAGE_NOT_FOUND = "error 404. Page not found";
|
|
276
349
|
var ALL_LOCATIONS = "*";
|
|
277
|
-
var Router = ({ routeList, context: initialContext = {} }) => {
|
|
350
|
+
var Router = ({ routeList, context: initialContext = {}, isAnimated = false, animationOptions = {} }) => {
|
|
278
351
|
const [location, setLocation] = useState(parseWindowLocation(window.location));
|
|
279
352
|
const [context, setContext] = useState(initialContext);
|
|
353
|
+
useApplyCustomAnimation(animationOptions);
|
|
280
354
|
const routeItem = useMemo(() => routeList.find((el) => el.path === ALL_LOCATIONS || comparePaths(el, location.pathname)), [location.pathname, routeList]);
|
|
281
355
|
const { loaderError, loaderCache, prefetchLoader, revalidateCache, isLoading } = useLoader(routeList);
|
|
282
356
|
const { blockerState, updateLocation, updateBlockedRoute } = useHandleNavigation({
|
|
283
357
|
setLocation,
|
|
284
358
|
routeList,
|
|
285
359
|
context,
|
|
286
|
-
revalidateCache
|
|
360
|
+
revalidateCache,
|
|
361
|
+
isAnimated
|
|
287
362
|
});
|
|
288
363
|
const params = useMemo(() => routeItem?.params ? getParamsObject(routeItem.params) : {}, [routeItem]);
|
|
289
364
|
const providerProps = useMemo(() => ({
|
|
@@ -306,7 +381,7 @@ var Router = ({ routeList, context: initialContext = {} }) => {
|
|
|
306
381
|
updateBlockedRoute,
|
|
307
382
|
updateLocation
|
|
308
383
|
]);
|
|
309
|
-
if (routeItem?.loader && !loaderError && isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterProvider, {
|
|
384
|
+
if (!isAnimated && routeItem?.loader && !loaderError && isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterProvider, {
|
|
310
385
|
...providerProps,
|
|
311
386
|
children: renderElement(routeItem?.loaderFallback)
|
|
312
387
|
});
|
|
@@ -314,9 +389,9 @@ var Router = ({ routeList, context: initialContext = {} }) => {
|
|
|
314
389
|
...providerProps,
|
|
315
390
|
children: renderElement(routeItem?.errorElement)
|
|
316
391
|
});
|
|
317
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
392
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(RouterProvider, {
|
|
318
393
|
...providerProps,
|
|
319
|
-
children: renderElement(routeItem?.element) || PAGE_NOT_FOUND
|
|
394
|
+
children: [renderElement(routeItem?.element) || PAGE_NOT_FOUND, isAnimated && isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {})]
|
|
320
395
|
});
|
|
321
396
|
};
|
|
322
397
|
//#endregion
|
|
@@ -415,55 +490,4 @@ var useRouterContext = () => {
|
|
|
415
490
|
};
|
|
416
491
|
};
|
|
417
492
|
//#endregion
|
|
418
|
-
|
|
419
|
-
function _typeof(o) {
|
|
420
|
-
"@babel/helpers - typeof";
|
|
421
|
-
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
422
|
-
return typeof o;
|
|
423
|
-
} : function(o) {
|
|
424
|
-
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
425
|
-
}, _typeof(o);
|
|
426
|
-
}
|
|
427
|
-
//#endregion
|
|
428
|
-
//#region \0@oxc-project+runtime@0.132.0/helpers/toPrimitive.js
|
|
429
|
-
function toPrimitive(t, r) {
|
|
430
|
-
if ("object" != _typeof(t) || !t) return t;
|
|
431
|
-
var e = t[Symbol.toPrimitive];
|
|
432
|
-
if (void 0 !== e) {
|
|
433
|
-
var i = e.call(t, r || "default");
|
|
434
|
-
if ("object" != _typeof(i)) return i;
|
|
435
|
-
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
436
|
-
}
|
|
437
|
-
return ("string" === r ? String : Number)(t);
|
|
438
|
-
}
|
|
439
|
-
//#endregion
|
|
440
|
-
//#region \0@oxc-project+runtime@0.132.0/helpers/toPropertyKey.js
|
|
441
|
-
function toPropertyKey(t) {
|
|
442
|
-
var i = toPrimitive(t, "string");
|
|
443
|
-
return "symbol" == _typeof(i) ? i : i + "";
|
|
444
|
-
}
|
|
445
|
-
//#endregion
|
|
446
|
-
//#region \0@oxc-project+runtime@0.132.0/helpers/defineProperty.js
|
|
447
|
-
function _defineProperty(e, r, t) {
|
|
448
|
-
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
449
|
-
value: t,
|
|
450
|
-
enumerable: !0,
|
|
451
|
-
configurable: !0,
|
|
452
|
-
writable: !0
|
|
453
|
-
}) : e[r] = t, e;
|
|
454
|
-
}
|
|
455
|
-
//#endregion
|
|
456
|
-
//#region utils/redirect.ts
|
|
457
|
-
var Redirect = class {
|
|
458
|
-
constructor(url, search) {
|
|
459
|
-
_defineProperty(this, "url", void 0);
|
|
460
|
-
_defineProperty(this, "search", void 0);
|
|
461
|
-
_defineProperty(this, "cause", void 0);
|
|
462
|
-
this.url = url;
|
|
463
|
-
this.search = search;
|
|
464
|
-
this.cause = "redirect";
|
|
465
|
-
}
|
|
466
|
-
};
|
|
467
|
-
var redirect = (url, search) => Promise.reject(new Redirect(url, search));
|
|
468
|
-
//#endregion
|
|
469
|
-
export { Link, Router, createRouter, redirect, useBeforeUnload, useBlocker, useLoaderState, useLocation, useNavigate, useParams, useRouterContext };
|
|
493
|
+
export { Link, Router, createRouter, useBeforeUnload, useBlocker, useLoaderState, useLocation, useNavigate, useParams, useRouterContext };
|
package/dist/types/global.d.ts
CHANGED
|
@@ -11,7 +11,10 @@ export type ClientRouteItem = {
|
|
|
11
11
|
fallback?: (() => ReactElement) | ReactElement;
|
|
12
12
|
children?: ClientRouteItem[];
|
|
13
13
|
staleTime?: number;
|
|
14
|
-
beforeLoad?: (
|
|
14
|
+
beforeLoad?: (arg: {
|
|
15
|
+
context: Record<string, unknown>;
|
|
16
|
+
redirect: (arg: Location) => Promise<void>;
|
|
17
|
+
}) => Promise<unknown> | undefined;
|
|
15
18
|
afterLoad?: (context: Record<string, unknown>) => Promise<unknown> | undefined;
|
|
16
19
|
};
|
|
17
20
|
export type RouteItem = ClientRouteItem & {
|
|
@@ -32,3 +35,7 @@ export type UpdateBlockedRouteProps = {
|
|
|
32
35
|
type: 'process' | 'reset' | 'charge' | 'unblock';
|
|
33
36
|
payload?: string;
|
|
34
37
|
};
|
|
38
|
+
export type AnimationOptions = {
|
|
39
|
+
duration?: number;
|
|
40
|
+
name?: 'fade' | 'slide-left' | 'slide-right';
|
|
41
|
+
};
|
package/package.json
CHANGED