clear-react-router 1.6.4 → 1.6.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 CHANGED
@@ -47,7 +47,8 @@ Normalizes route configuration. Handles wildcard `*` routes, extracts dynamic pa
47
47
  | `loaderFallback` | `ReactElement \| () => ReactElement` | Loading fallback (for loader) |
48
48
  | `errorElement` | `ReactElement \| () => ReactElement` | Error fallback |
49
49
  | `staleTime` | `number` | Time in ms before cached data is considered stale and re-fetched in the background. If not provided, data never expires (cached forever) |
50
- | `children` | `RouteItem[]` | Nested routes |
50
+ | `actions` | `({ params, context, invalidate, setContext }) => Record<string, (formData: FormData) => unknown \| Promise<unknown>>` | Defines route actions for data mutations. Actions receive `FormData`, can update context via `setContext`, and can refresh loader data using the router-provided `invalidate`. |
51
+
51
52
 
52
53
  ### `RouterProvider`
53
54
 
@@ -56,10 +57,9 @@ The root component that provides routing context to the application. Place stati
56
57
  | Prop | Type | Default | Description |
57
58
  |------|------|---------|-------------|
58
59
  | `routes` | `RouteItem[]` | required | Array of route configurations |
59
- | `context` | `object` | `{}` | Initial context (user, theme, etc.) |
60
60
  | `children` | `ReactNode` | required | App content (must include `<Router />`) |
61
61
 
62
- ```
62
+ ```tsx
63
63
  function App() {
64
64
  return (
65
65
  <RouterProvider routeList={routes}>
@@ -86,9 +86,10 @@ Renders the current route's component. Must be placed inside `<RouterProvider>`.
86
86
  | `showFallbackOnAnimation` | `boolean \| undefined` | `false` | Show `loaderFallback` even when `isAnimated` is `true` (instead of spinner) |
87
87
  | `prefetch` | `'hover' \| 'render' \| 'viewport' \| 'none'` | `'hover'` | Default prefetch strategy for all `<Link>` components |
88
88
  | `hoverPrefetchDelay` | `number` | `150` | Delay in milliseconds before prefetching on hover (only for `'hover'` strategy) |
89
+ | `context` | `object` | `{}` | Initial context (user, theme, etc.) |
89
90
  | `errorBoundary` | `ComponentType<{ children: ReactNode }>` | `undefined` | Custom error boundary component for catching render errors in route components |
90
91
 
91
- ```
92
+ ```tsx
92
93
  <RouterProvider routes={routes}>
93
94
  <Navbar />
94
95
  <Router spinner={false} isAnimated /> {/* disable the spinner */}
@@ -119,7 +120,7 @@ Component for client-side navigation with prefetch support.
119
120
 
120
121
  **Example:**
121
122
 
122
- ```
123
+ ```tsx
123
124
  import { RouterProvider, Router, Link } from 'clear-react-router';
124
125
 
125
126
  // Global prefetch: hover with 100ms delay
@@ -145,7 +146,7 @@ Function provided to `beforeLoad` for programmatic redirection.
145
146
 
146
147
  **Type:** `(arg: Location | string) => Promise<void>`
147
148
 
148
- ```
149
+ ```tsx
149
150
  import type { createRouter } from 'clear-react-router';
150
151
 
151
152
  const routes = createRouter([
@@ -189,7 +190,9 @@ const routes = createRouter([
189
190
 
190
191
  The `loader`, `beforeLoad`, and `afterLoad` hooks receive `params` (extracted from the URL) and `context` as arguments. This allows you to handle route-specific logic directly in the route configuration, keeping your components focused on rendering.
191
192
 
192
- ```
193
+ ```tsx
194
+ import type { createRouter } from 'clear-react-router';
195
+
193
196
  const routes = createRouter([
194
197
  {
195
198
  path: '/user/:userId',
@@ -217,6 +220,114 @@ const routes = createRouter([
217
220
  ]);
218
221
  ```
219
222
 
223
+ ## Route Actions
224
+
225
+ Defines route-specific actions for handling data mutations such as creating, updating, or deleting resources.
226
+
227
+ Actions are available through the `Form` component and the `useAction` hook. After a successful action, the current route is automatically invalidated, causing both `beforeLoad` and `loader` to run again in the background.
228
+
229
+ ```tsx
230
+ actions?: ({ context, params, invalidate, setContext }) => ({
231
+ save: async (formData) => {
232
+ await api.updatePost(params.id, formData);
233
+ },
234
+
235
+ remove: async () => {
236
+ await api.deletePost(params.id);
237
+ },
238
+ })
239
+ ```
240
+
241
+ #### Arguments
242
+
243
+ | Property | Type | Description |
244
+ | ------------ | --------------------------------------------------- | -------------------------------------------------------------------------------------------- |
245
+ | `context` | `Record<string, unknown>` | Current router context. |
246
+ | `params` | `Record<string, string>` | Route parameters. |
247
+ | `invalidate` | `(path?: string) => Promise<void>` | Invalidates the current route or a specific route, re-running its `beforeLoad` and `loader`. |
248
+ | `setContext` | `Dispatch<SetStateAction<Record<string, unknown>>>` | Updates the router context. |
249
+
250
+ #### Returns
251
+
252
+ A record where each key is an action name and each value is a function accepting a `FormData` instance.
253
+
254
+ These action names are referenced by both `Form` and `useAction`.
255
+
256
+ ```tsx
257
+ <Form action="save" />
258
+
259
+ const save = useAction('save');
260
+ ```
261
+
262
+
263
+ Actions can be executed declaratively with `<Form />` or imperatively with `useAction()`.
264
+
265
+ ## Form
266
+
267
+ `Form` automatically creates a `FormData` object, executes the specified route action, invalidates the current route, and optionally resets the form.
268
+
269
+ `isSubmitting` value available inside the `Form` component from the `useFormContext` hook
270
+
271
+ ```tsx
272
+ import { Form, useFormContext } from '../clear-router';
273
+
274
+ const SubmitButton = () => {
275
+ const {isSubmitting} = useFormContext()
276
+ return <button disabled={isSubmitting} type='submit'>Save</button>
277
+ }
278
+
279
+ <Form action="save" onSuccess={() => console.log('Saved')} onError={console.error}>
280
+ <input name="title" />
281
+ <SubmitButton />
282
+ </Form>
283
+
284
+ ```
285
+
286
+ ### Props
287
+
288
+ | Prop | Type | Description |
289
+ | ----------- | --------------------------- | -------------------------------------------------------------------------------- |
290
+ | `action` | `string` | Name of the route action to execute. |
291
+ | `onSuccess` | `(result: unknown) => void` | Called when the action completes successfully. Receives the action return value. |
292
+ | `onError` | `(error: unknown) => void` | Called when the action throws. |
293
+ | `autoReset` | `boolean` | Automatically resets the form after a successful submission. Default: `true`. |
294
+
295
+ During submission, `Form` exposes the current submission state through `useFormContext()`.
296
+
297
+ After a successful action:
298
+
299
+ * the current route is invalidated;
300
+ * `beforeLoad` is executed again;
301
+ * `loader` is executed again;
302
+ * fresh loader data becomes available.
303
+
304
+ ## useAction
305
+
306
+ `useAction` provides direct access to a route action without rendering a `<Form />`.
307
+
308
+ ```tsx
309
+ const save = useAction('save');
310
+
311
+ const handleClick = async () => {
312
+ const data = new FormData();
313
+
314
+ data.append('title', 'Hello');
315
+
316
+ await save(data);
317
+ };
318
+ ```
319
+
320
+ ```tsx
321
+ <button onClick={handleClick}>
322
+ Save
323
+ </button>
324
+ ```
325
+
326
+ `useAction` automatically invalidates the current route after a successful action, causing both `beforeLoad` and `loader` to run again in the background.
327
+
328
+ This hook is useful when the mutation is triggered programmatically, such as from dialogs, context menus, drag-and-drop interactions, keyboard shortcuts, or custom UI components.
329
+
330
+
220
331
  ### Error Boundaries
221
332
 
222
333
  You can provide a custom error boundary to catch rendering errors in route components. This is useful for preventing the entire app from crashing when a specific route fails to render.
@@ -238,19 +349,21 @@ const App = () => (
238
349
 
239
350
  ### `useNavigate()`
240
351
 
241
- Returns function to navigate programmatically. Accepts a string (pathname), an object with `pathname`, `search`, and `state`, or `-1` to go back.
352
+ Returns function to navigate programmatically. Accepts a string (pathname), an object of type Location, or `-1` to go back.
353
+
354
+ ```tsx
355
+ type Location = { pathname: string; search?: string; state?: unknown }
242
356
 
243
- ```
244
357
  const navigate = useNavigate();
245
358
 
246
359
  navigate('/about'); // string
247
- navigate({ pathname: '/user/123', state: { from: 'home' } }); // object
360
+ navigate({ pathname: '/user/123', state: { from: 'home' } }); // Location
248
361
  navigate(-1); // go back
249
362
  ```
250
363
 
251
364
  **Note:** Navigation state can be accessed via `useLocation()`:
252
365
 
253
- ```
366
+ ```tsx
254
367
  const navigate = useNavigate();
255
368
  navigate({ pathname: '/profile', state: { userId: 123 } });
256
369
 
@@ -263,7 +376,7 @@ console.log(state); // { userId: 123 }
263
376
 
264
377
  Returns route parameters object.
265
378
 
266
- ```
379
+ ```tsx
267
380
  const params = useParams<{ userId: string }>();
268
381
  // URL: /user/123 → params.userId === '123'
269
382
  ```
@@ -271,7 +384,7 @@ const params = useParams<{ userId: string }>();
271
384
  ### `useLocation()`
272
385
 
273
386
  Returns current location `{ pathname, search, state }`.
274
- ```
387
+ ```tsx
275
388
  const { pathname, search, state } = useLocation();
276
389
  ```
277
390
 
@@ -287,7 +400,7 @@ Returns the cached data loaded by the current route's `loader`, along with any e
287
400
  | `loaderError` | `Error \| null` | Error from the `loader` (if any) |
288
401
  | `beforeLoadError` | `Error \| null` | Error from the `beforeLoad` hook (if any) |
289
402
 
290
- ```
403
+ ```tsx
291
404
  const UserProfile = () => {
292
405
  const { data, loaderError, beforeLoadError } = useLoaderState<User>();
293
406
  ```
@@ -380,7 +493,7 @@ Blocks navigation when callback returns `true`.
380
493
  | `process()` | `() => void` | Confirm navigation and proceed |
381
494
  | `reset()` | `() => void` | Cancel navigation |
382
495
 
383
- ```
496
+ ```tsx
384
497
  const { state, process, reset } = useBlocker(() => hasUnsavedChanges);
385
498
 
386
499
  useEffect(() => {
@@ -407,7 +520,7 @@ Executes a callback when the page is about to be closed or reloaded. Perfect for
407
520
 
408
521
  **Note:** This hook does not show a browser confirmation dialog. It silently executes the callback, allowing you to save user data in the background before the page closes.
409
522
 
410
- ```
523
+ ```tsx
411
524
  const [text, setText] = useState('');
412
525
  const onSave = useCallback(() => {
413
526
  localStorage.setItem('draft', text);
@@ -422,7 +535,7 @@ useBeforeUnload(text ? onSave : undefined);
422
535
 
423
536
  A flexible hook for working with typed query parameters. You provide an adapter object with `parse` and `serialize` functions, and it returns the parsed value and a setter.
424
537
 
425
- ```
538
+ ```tsx
426
539
  import { useQueryParam, adapter } from 'clear-react-router';
427
540
 
428
541
  const ProductPage = () => {
@@ -486,7 +599,7 @@ type Adapter<T> = {
486
599
  ### Using Zod Schemas
487
600
  `useQueryParam` works seamlessly with Zod for complex validation:
488
601
 
489
- ```
602
+ ```tsx
490
603
  import { z } from 'zod';
491
604
  import { useQueryParam, adapter } from 'clear-react-router';
492
605
 
@@ -518,7 +631,7 @@ function ProductFilter() {
518
631
  ### Custom Adapters
519
632
  You can write your own adapter for any format:
520
633
 
521
- ```
634
+ ```tsx
522
635
  // Custom adapter for comma-separated values
523
636
  const csvAdapter = {
524
637
  parse: (params: string[]): string[] => {
@@ -537,7 +650,7 @@ const TagsFilter() {
537
650
  ### `useRouterContext()`
538
651
 
539
652
  Returns the router context object and a function to update it. Useful for accessing or modifying global state (like user authentication, theme, etc.) from anywhere in your app.
540
- ```
653
+ ```tsx
541
654
  const { setContext, context } = useRouterContext();
542
655
  const loginHandler = () => setContext({ ...context, user: { name: 'John' } });
543
656
  ```
@@ -546,7 +659,7 @@ const loginHandler = () => setContext({ ...context, user: { name: 'John' } });
546
659
 
547
660
  Returns an object for working with URL query parameters. Supports reading and setting both single values and arrays.
548
661
 
549
- ```
662
+ ```tsx
550
663
  import { useSearchParams } from 'clear-react-router';
551
664
 
552
665
  function ProductFilter() {
@@ -603,7 +716,7 @@ Returns an array of pathnames representing the user's actual navigation history.
603
716
  ## Lazy Loading
604
717
 
605
718
  Clear Router supports code-splitting out of the box. Simply pass a function that returns a dynamic import:
606
- ```
719
+ ```tsx
607
720
  {
608
721
  path: '/heavy-page',
609
722
  element: () => import('./pages/HeavyComponent'),
@@ -0,0 +1,9 @@
1
+ import { PropsWithChildren } from 'react';
2
+ type FormProps = {
3
+ action: string;
4
+ onSuccess?(arg: unknown): void;
5
+ onError?(arg: unknown): void;
6
+ autoReset?: boolean;
7
+ };
8
+ export declare const Form: ({ children, action: actionKey, onSuccess, onError, autoReset, }: PropsWithChildren<FormProps>) => import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -1,2 +1,2 @@
1
1
  import { RouterProps } from '../types/global';
2
- export declare const Router: ({ isAnimated, animationDuration, spinner, preserveScroll, showFallbackOnAnimation, prefetch, hoverPrefetchDelay, errorBoundary: ErrorBoundary, }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
2
+ export declare const Router: ({ isAnimated, animationDuration, spinner, preserveScroll, showFallbackOnAnimation, prefetch, hoverPrefetchDelay, errorBoundary: ErrorBoundary, context: initialContext, }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
@@ -3,7 +3,6 @@ import { RouteItem } from '../types/global';
3
3
  type RouteProviderProps = {
4
4
  children: ReactNode;
5
5
  routes: RouteItem[];
6
- context?: Record<string, unknown>;
7
6
  };
8
- export declare const RouterProvider: ({ children, routes, context: initialContext }: RouteProviderProps) => import("react/jsx-runtime").JSX.Element;
7
+ export declare const RouterProvider: ({ children, routes }: RouteProviderProps) => import("react/jsx-runtime").JSX.Element;
9
8
  export {};
@@ -0,0 +1,4 @@
1
+ export type FormContextProps = {
2
+ isSubmitting: boolean;
3
+ };
4
+ export declare const FormContext: import("react").Context<FormContextProps>;
@@ -1,22 +1,7 @@
1
- import { BlockerState, LoaderState, Location, RouteItem, RouteItemData, UpdateBlockedRouteProps } from '../types/global';
2
- export type NavigationContextValue = {
3
- blockerState: BlockerState;
4
- routeItemData: RouteItemData;
5
- currentLoaderFallback: RouteItem['loaderFallback'];
6
- isLoading: boolean;
7
- loaderState: LoaderState;
8
- };
1
+ import { Location } from '../types/global';
9
2
  export type ActionsContextValue = {
10
3
  updateLocation(route: Location): Promise<void>;
11
- updateBlockedRoute(arg: UpdateBlockedRouteProps): void;
12
4
  prefetchLoader(arg: string): Promise<void>;
13
- setContext(arg: object): void;
14
- restoreScroll(): void;
15
- invalidate(path?: string): void;
16
- };
17
- export type DataContextValue = {
18
- context: Record<string, unknown>;
5
+ invalidate(path?: string): Promise<void>;
19
6
  };
20
7
  export declare const ActionsContext: import("react").Context<ActionsContextValue>;
21
- export declare const DataContext: import("react").Context<DataContextValue>;
22
- export declare const NavigationContext: import("react").Context<NavigationContextValue>;
@@ -0,0 +1 @@
1
+ export declare const useAction: (actionKey: string, onError?: (args: unknown) => void) => (formData: FormData) => Promise<void>;
@@ -1,4 +1,4 @@
1
- import type { BlockerState } from '../types/global.ts';
1
+ import { BlockerState } from '../types/global';
2
2
  type UseBlockerReturnValue = {
3
3
  state: BlockerState;
4
4
  process(): void;
@@ -0,0 +1 @@
1
+ export declare const useFormContext: () => import("../context/FormContext").FormContextProps;
@@ -1 +1 @@
1
- export declare const useInvalidate: () => (path?: string) => void;
1
+ export declare const useInvalidate: () => (path?: string) => Promise<void>;
@@ -1,11 +1,5 @@
1
- import { type Dispatch, type SetStateAction } from 'react';
2
1
  import type { LoaderState, RevalidateCacheArgs, RouteItem } from '../types/global';
3
- type UseLoaderParams = {
4
- routes: RouteItem[];
5
- context: Record<string, unknown>;
6
- setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
7
- };
8
- export declare const useLoader: ({ routes, context, setContext }: UseLoaderParams) => {
2
+ export declare const useLoader: (routes: RouteItem[]) => {
9
3
  prefetchLoader: (pathname: string) => Promise<void>;
10
4
  revalidateCache: ({ routeItem, pathname }: RevalidateCacheArgs) => Promise<unknown>;
11
5
  isCacheItemFresh: ({ routeItem, pathname }: {
@@ -13,6 +7,5 @@ export declare const useLoader: ({ routes, context, setContext }: UseLoaderParam
13
7
  pathname: string;
14
8
  }) => boolean;
15
9
  loaderStateRef: import("react").RefObject<LoaderState>;
16
- clearTimestamp: (pathname: string) => void;
10
+ invalidate: (pathname?: string) => Promise<void>;
17
11
  };
18
- export {};
@@ -1,2 +1,2 @@
1
1
  import type { Location } from '../types/global';
2
- export declare const useNavigate: () => (arg: Location | string | number) => Promise<void>;
2
+ export declare const useNavigate: () => (arg: Location | string | -1) => Promise<void>;
@@ -0,0 +1,13 @@
1
+ import { RefObject } from 'react';
2
+ import { LoaderState, Location, RevalidateCacheArgs, RouteItem } from '../types/global';
3
+ type UseHandleNavigation = {
4
+ routes: RouteItem[];
5
+ revalidateCache(arg: RevalidateCacheArgs): Promise<unknown>;
6
+ isCacheItemFresh(arg: {
7
+ routeItem?: RouteItem;
8
+ pathname: string;
9
+ }): boolean;
10
+ loaderStateRef: RefObject<LoaderState>;
11
+ };
12
+ export declare const useNavigation: ({ routes, revalidateCache, isCacheItemFresh, loaderStateRef }: UseHandleNavigation) => (nextLocation: Location) => Promise<void>;
13
+ export {};
@@ -1,4 +1,4 @@
1
1
  export declare const useRouterContext: () => {
2
2
  context: Record<string, unknown>;
3
- setContext: (arg: object) => void;
3
+ setContext: (action: Record<string, unknown> | ((prevState: Record<string, unknown>) => Record<string, unknown>)) => void;
4
4
  };
@@ -1,3 +1 @@
1
- export declare const useNavigationState: () => import("../context/RouterProviderContext").NavigationContextValue;
2
1
  export declare const useRouterActions: () => import("../context/RouterProviderContext").ActionsContextValue;
3
- export declare const useRouterData: () => import("../context/RouterProviderContext").DataContextValue;
@@ -0,0 +1 @@
1
+ export declare const useSetInitialContext: (initialContext?: Record<string, unknown>) => void;
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export { useRouterContext } from './hooks/useRouterContext';
12
12
  export { useQueryParam } from './hooks/useQueryParam';
13
13
  export { useSearchParams } from './hooks/useSearchParams';
14
14
  export { useHistoricalTrail } from './hooks/useHistoricalTrail';
15
+ export { useFormContext } from './hooks/useFormContext';
15
16
  export { adapter } from './utils/adapter';
16
17
  export { createRouter } from './utils/utils';
17
18
  export type { RouteItem, BlockerState, Location, AdapterType, RouterProps } from './types/global';
package/dist/index.js CHANGED
@@ -4,8 +4,6 @@ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).expor
4
4
  //#endregion
5
5
  //#region context/RouterProviderContext.ts
6
6
  var ActionsContext = createContext({});
7
- var DataContext = createContext({});
8
- var NavigationContext = createContext({});
9
7
  //#endregion
10
8
  //#region ../../node_modules/react/cjs/react-jsx-runtime.production.js
11
9
  /**
@@ -45,29 +43,14 @@ var require_react_jsx_runtime_production = /* @__PURE__ */ __commonJSMin(((expor
45
43
  var import_jsx_runtime = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
46
44
  module.exports = require_react_jsx_runtime_production();
47
45
  })))();
48
- var Provider = ({ children, setContext, context, updateBlockedRoute, updateLocation, prefetchLoader, blockerState, routeItemData, restoreScroll, currentLoaderFallback, isLoading, loaderState, invalidate }) => {
46
+ var Provider = ({ children, updateLocation, prefetchLoader, invalidate }) => {
49
47
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActionsContext.Provider, {
50
48
  value: {
51
49
  updateLocation,
52
- updateBlockedRoute,
53
50
  prefetchLoader,
54
- setContext,
55
- restoreScroll,
56
51
  invalidate
57
52
  },
58
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DataContext.Provider, {
59
- value: { context },
60
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavigationContext.Provider, {
61
- value: {
62
- blockerState,
63
- routeItemData,
64
- currentLoaderFallback,
65
- isLoading,
66
- loaderState
67
- },
68
- children
69
- })
70
- })
53
+ children
71
54
  });
72
55
  };
73
56
  //#endregion
@@ -80,10 +63,56 @@ var useLatest = (value) => {
80
63
  return ref;
81
64
  };
82
65
  //#endregion
66
+ //#region state/createState.ts
67
+ var create = (initialState) => {
68
+ let state = initialState;
69
+ const subscribers = /* @__PURE__ */ new Set();
70
+ const getState = () => state;
71
+ const setState = (action) => {
72
+ const prevState = state;
73
+ const nextState = typeof action === "function" ? action(state) : action;
74
+ if (!Object.is(state, nextState)) {
75
+ state = nextState;
76
+ subscribers.forEach((listener) => listener(state, prevState));
77
+ }
78
+ };
79
+ const subscribe = (listener) => {
80
+ subscribers.add(listener);
81
+ return () => subscribers.delete(listener);
82
+ };
83
+ return {
84
+ subscribe,
85
+ getState,
86
+ setState
87
+ };
88
+ };
89
+ var useGlobalState = ({ subscribe, getState, setState }) => {
90
+ return [useSyncExternalStore(subscribe, getState), setState];
91
+ };
92
+ var createState = (initialState) => {
93
+ const store = create(initialState);
94
+ return () => useGlobalState(store);
95
+ };
96
+ //#endregion
83
97
  //#region constants.ts
84
98
  var emptyLoaderState = {};
85
99
  //#endregion
86
- //#region \0@oxc-project+runtime@0.133.0/helpers/esm/typeof.js
100
+ //#region state/state.ts
101
+ var useIsLoading = createState(false);
102
+ var useBlockedRoute = createState({
103
+ from: "",
104
+ to: ""
105
+ });
106
+ var useLoaderFallback = createState(void 0);
107
+ var useRouteItemData = createState({
108
+ routeItem: void 0,
109
+ location: {}
110
+ });
111
+ var useCurrentLoaderState = createState(emptyLoaderState);
112
+ var useScrollMap = createState({});
113
+ var useContextState = createState({});
114
+ //#endregion
115
+ //#region \0@oxc-project+runtime@0.132.0/helpers/typeof.js
87
116
  function _typeof(o) {
88
117
  "@babel/helpers - typeof";
89
118
  return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
@@ -93,7 +122,7 @@ function _typeof(o) {
93
122
  }, _typeof(o);
94
123
  }
95
124
  //#endregion
96
- //#region \0@oxc-project+runtime@0.133.0/helpers/esm/toPrimitive.js
125
+ //#region \0@oxc-project+runtime@0.132.0/helpers/toPrimitive.js
97
126
  function toPrimitive(t, r) {
98
127
  if ("object" != _typeof(t) || !t) return t;
99
128
  var e = t[Symbol.toPrimitive];
@@ -105,13 +134,13 @@ function toPrimitive(t, r) {
105
134
  return ("string" === r ? String : Number)(t);
106
135
  }
107
136
  //#endregion
108
- //#region \0@oxc-project+runtime@0.133.0/helpers/esm/toPropertyKey.js
137
+ //#region \0@oxc-project+runtime@0.132.0/helpers/toPropertyKey.js
109
138
  function toPropertyKey(t) {
110
139
  var i = toPrimitive(t, "string");
111
140
  return "symbol" == _typeof(i) ? i : i + "";
112
141
  }
113
142
  //#endregion
114
- //#region \0@oxc-project+runtime@0.133.0/helpers/esm/defineProperty.js
143
+ //#region \0@oxc-project+runtime@0.132.0/helpers/defineProperty.js
115
144
  function _defineProperty(e, r, t) {
116
145
  return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
117
146
  value: t,
@@ -186,34 +215,19 @@ var comparePaths = (el, pathname) => {
186
215
  return splitElementPath.every((item, index) => item === splitPathname[index + (index ? 1 : 0)]) && splitPathname.length === splitElementPath.length + paramsLength;
187
216
  };
188
217
  //#endregion
189
- //#region hooks/useHandleNavigation.ts
218
+ //#region hooks/useNavigation.ts
190
219
  var ALL_LOCATIONS = "*";
191
- var useHandleNavigation = ({ routes, context, revalidateCache, setContext, isCacheItemFresh, loaderStateRef, clearTimestamp }) => {
220
+ var useNavigation = ({ routes, revalidateCache, isCacheItemFresh, loaderStateRef }) => {
221
+ const [, setIsLoading] = useIsLoading();
222
+ const [, setScrollMap] = useScrollMap();
223
+ const [, setRouteItemData] = useRouteItemData();
224
+ const [, setLoaderState] = useCurrentLoaderState();
225
+ const [, setCurrentLoaderFallback] = useLoaderFallback();
226
+ const [context, setContext] = useContextState();
227
+ const [blockedRoute, setBlockedRoute] = useBlockedRoute();
192
228
  const { isAnimated, showFallbackOnAnimation: showFallback } = routerConfig;
193
- const [isLoading, setIsLoading] = useState(false);
194
- const [blockedRoute, setBlockedRoute] = useState({
195
- from: "",
196
- to: ""
197
- });
198
- const [routeItemData, setRouteItemData] = useState({
199
- routeItem: void 0,
200
- location: {}
201
- });
202
- const [scrollMap, setScrollMap] = useState({});
203
- const [currentLoaderFallback, setCurrentLoaderFallback] = useState();
204
- const [loaderState, setLoaderState] = useState(emptyLoaderState);
205
229
  const prevPathname = useRef("");
206
230
  const navigationSeq = useRef(0);
207
- const scrollMapLatest = useLatest(scrollMap);
208
- const restoreScroll = useCallback(() => {
209
- if (!prevPathname.current || !scrollMapLatest.current[prevPathname.current]) return;
210
- requestAnimationFrame(() => {
211
- window.scrollTo({
212
- top: scrollMapLatest.current[prevPathname.current],
213
- behavior: "smooth"
214
- });
215
- });
216
- }, [scrollMapLatest]);
217
231
  const navigation = useCallback((nextLocation, routeItem) => {
218
232
  setRouteItemData({
219
233
  routeItem,
@@ -226,7 +240,13 @@ var useHandleNavigation = ({ routes, context, revalidateCache, setContext, isCac
226
240
  const fullPath = nextLocation.search ? `${nextLocation.pathname}${nextLocation.search}` : nextLocation.pathname;
227
241
  if (fullPath === window.location.pathname + window.location.search) return;
228
242
  history.pushState(null, "", fullPath);
229
- }, [loaderStateRef]);
243
+ }, [
244
+ loaderStateRef,
245
+ setCurrentLoaderFallback,
246
+ setIsLoading,
247
+ setLoaderState,
248
+ setRouteItemData
249
+ ]);
230
250
  const transitionedNavigation = useCallback((nextLocation, routeItem) => {
231
251
  if (!isAnimated) {
232
252
  navigation(nextLocation, routeItem);
@@ -238,45 +258,6 @@ var useHandleNavigation = ({ routes, context, revalidateCache, setContext, isCac
238
258
  navigation(nextLocation, routeItem);
239
259
  }
240
260
  }, [navigation, isAnimated]);
241
- const invalidate = useCallback(async (pathname = routeItemData.location.pathname) => {
242
- if (typeof pathname !== "string") return;
243
- const routeItem = routes.find((el) => comparePaths(el, pathname));
244
- const resultParams = getParamsObject({
245
- params: routeItem?.params,
246
- pathname
247
- });
248
- clearTimestamp(pathname);
249
- try {
250
- if (routeItem?.beforeLoad) await routeItem.beforeLoad({
251
- context,
252
- redirect: () => Promise.resolve(),
253
- params: resultParams,
254
- setContext
255
- });
256
- loaderStateRef.current = {
257
- ...loaderStateRef.current,
258
- beforeLoadError: null
259
- };
260
- } catch (error) {
261
- loaderStateRef.current = {
262
- ...loaderStateRef.current,
263
- beforeLoadError: error
264
- };
265
- }
266
- await revalidateCache({
267
- routeItem,
268
- pathname
269
- });
270
- if (pathname === routeItemData.location.pathname) setLoaderState(loaderStateRef.current);
271
- }, [
272
- clearTimestamp,
273
- context,
274
- loaderStateRef,
275
- revalidateCache,
276
- routeItemData.location.pathname,
277
- routes,
278
- setContext
279
- ]);
280
261
  const navigationHandler = useCallback(async (nextLocation) => {
281
262
  navigationSeq.current = navigationSeq.current + 1;
282
263
  const seq = navigationSeq.current;
@@ -336,40 +317,20 @@ var useHandleNavigation = ({ routes, context, revalidateCache, setContext, isCac
336
317
  });
337
318
  }, [
338
319
  context,
320
+ isAnimated,
321
+ isCacheItemFresh,
322
+ loaderStateRef,
339
323
  revalidateCache,
340
324
  routes,
341
- transitionedNavigation,
342
325
  setContext,
343
- isCacheItemFresh,
344
- isAnimated,
326
+ setCurrentLoaderFallback,
327
+ setIsLoading,
328
+ setScrollMap,
345
329
  showFallback,
346
- loaderStateRef
330
+ transitionedNavigation
347
331
  ]);
348
332
  const setNextLocationRef = useLatest(navigationHandler);
349
- const updateBlockedRoute = useCallback(({ type, payload = "" }) => setBlockedRoute((prevState) => {
350
- if (prevState.from === payload && type === "charge") return prevState;
351
- if (payload && prevState.from !== payload && type === "charge") return {
352
- ...prevState,
353
- from: payload
354
- };
355
- if (type === "reset") return {
356
- ...prevState,
357
- to: ""
358
- };
359
- if (type === "process") setNextLocationRef.current({ pathname: prevState.to });
360
- if (!prevState.from && !prevState.to) return prevState;
361
- return {
362
- from: "",
363
- to: ""
364
- };
365
- }), [setNextLocationRef]);
366
- const updateLocation = useCallback(async (nextLocation) => {
367
- if (blockedRoute.from) setBlockedRoute((prevState) => ({
368
- ...prevState,
369
- to: nextLocation.pathname
370
- }));
371
- else await setNextLocationRef.current(nextLocation);
372
- }, [blockedRoute.from, setNextLocationRef]);
333
+ const updateLocation = useCallback(async (nextLocation) => await setNextLocationRef.current(nextLocation), [setNextLocationRef]);
373
334
  useEffect(() => {
374
335
  const handler = async (event) => {
375
336
  const newLocation = parseWindowLocation(event.target.location);
@@ -383,34 +344,28 @@ var useHandleNavigation = ({ routes, context, revalidateCache, setContext, isCac
383
344
  };
384
345
  window.addEventListener("popstate", handler);
385
346
  return () => window.removeEventListener("popstate", handler);
386
- }, [blockedRoute.from, setNextLocationRef]);
347
+ }, [
348
+ blockedRoute.from,
349
+ setBlockedRoute,
350
+ setNextLocationRef
351
+ ]);
387
352
  useEffect(() => {
388
353
  const currentLocation = parseWindowLocation(window.location);
389
354
  setNextLocationRef.current(currentLocation);
390
355
  prevPathname.current = currentLocation.pathname;
391
356
  }, [setNextLocationRef]);
392
- return {
393
- blockerState: useMemo(() => {
394
- if (blockedRoute.from && blockedRoute.to) return "blocked";
395
- if (blockedRoute.from) return "charged";
396
- return "unblocked";
397
- }, [blockedRoute]),
398
- updateLocation,
399
- updateBlockedRoute,
400
- routeItemData,
401
- restoreScroll,
402
- currentLoaderFallback,
403
- isLoading,
404
- loaderState,
405
- invalidate
406
- };
357
+ return updateLocation;
407
358
  };
408
359
  //#endregion
409
360
  //#region hooks/useLoader.ts
410
- var useLoader = ({ routes, context, setContext }) => {
361
+ var useLoader = (routes) => {
362
+ const [, setLoaderState] = useCurrentLoaderState();
363
+ const [routeItemData] = useRouteItemData();
364
+ const [context, setContext] = useContextState();
365
+ const loaderStateRef = useRef(emptyLoaderState);
366
+ const latestPathname = useLatest(routeItemData.location.pathname);
411
367
  const timestampMapRef = useRef(/* @__PURE__ */ new Map());
412
368
  const loaderMapRef = useRef({});
413
- const loaderStateRef = useRef(emptyLoaderState);
414
369
  const loadingPromises = useRef(/* @__PURE__ */ new Map());
415
370
  const isCacheItemFresh = useCallback(({ routeItem, pathname }) => {
416
371
  if (!routeItem) return true;
@@ -476,70 +431,64 @@ var useLoader = ({ routes, context, setContext }) => {
476
431
  revalidateCache,
477
432
  isCacheItemFresh,
478
433
  loaderStateRef,
479
- clearTimestamp: useCallback((pathname) => {
434
+ invalidate: useCallback(async (pathname = latestPathname.current) => {
435
+ if (typeof pathname !== "string") return;
436
+ const routeItem = routes.find((el) => comparePaths(el, pathname));
437
+ const resultParams = getParamsObject({
438
+ params: routeItem?.params,
439
+ pathname
440
+ });
480
441
  timestampMapRef.current.delete(pathname);
481
- }, [])
442
+ try {
443
+ if (routeItem?.beforeLoad) await routeItem.beforeLoad({
444
+ context,
445
+ redirect: () => Promise.resolve(),
446
+ params: resultParams,
447
+ setContext
448
+ });
449
+ loaderStateRef.current = {
450
+ ...loaderStateRef.current,
451
+ beforeLoadError: null
452
+ };
453
+ } catch (error) {
454
+ loaderStateRef.current = {
455
+ ...loaderStateRef.current,
456
+ beforeLoadError: error
457
+ };
458
+ }
459
+ await revalidateCache({
460
+ routeItem,
461
+ pathname
462
+ });
463
+ if (pathname === latestPathname.current) setLoaderState(loaderStateRef.current);
464
+ }, [
465
+ context,
466
+ loaderStateRef,
467
+ revalidateCache,
468
+ latestPathname,
469
+ routes,
470
+ setContext,
471
+ setLoaderState
472
+ ])
482
473
  };
483
474
  };
484
475
  //#endregion
485
476
  //#region components/RouterProvider.tsx
486
- var RouterProvider = ({ children, routes, context: initialContext = {} }) => {
487
- const [context, setContext] = useState(initialContext);
488
- const { prefetchLoader, revalidateCache, isCacheItemFresh, loaderStateRef, clearTimestamp } = useLoader({
489
- routes,
490
- context,
491
- setContext
492
- });
493
- const { blockerState, updateLocation, updateBlockedRoute, routeItemData, restoreScroll, currentLoaderFallback, isLoading, loaderState, invalidate } = useHandleNavigation({
494
- routes,
495
- context,
496
- setContext,
497
- revalidateCache,
498
- isCacheItemFresh,
499
- loaderStateRef,
500
- clearTimestamp
501
- });
477
+ var RouterProvider = ({ children, routes }) => {
478
+ const { prefetchLoader, revalidateCache, isCacheItemFresh, loaderStateRef, invalidate } = useLoader(routes);
502
479
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Provider, {
503
- ...useMemo(() => ({
504
- updateLocation,
505
- prefetchLoader,
506
- updateBlockedRoute,
507
- blockerState,
508
- context,
509
- setContext,
510
- routeItemData,
511
- restoreScroll,
512
- currentLoaderFallback,
513
- isLoading,
514
- loaderState,
515
- invalidate
516
- }), [
517
- blockerState,
518
- context,
519
- prefetchLoader,
520
- routeItemData,
521
- updateBlockedRoute,
522
- updateLocation,
523
- currentLoaderFallback,
524
- restoreScroll,
525
- isLoading,
526
- loaderState,
527
- invalidate
528
- ]),
480
+ updateLocation: useNavigation({
481
+ routes,
482
+ revalidateCache,
483
+ isCacheItemFresh,
484
+ loaderStateRef
485
+ }),
486
+ invalidate,
487
+ prefetchLoader,
529
488
  children
530
489
  });
531
490
  };
532
491
  //#endregion
533
- //#region hooks/useServiceContext.ts
534
- var useServiceState = (reactContext) => {
535
- const context = useContext(reactContext);
536
- if (!Object.keys(context).length) throw new Error("hooks and Router component must be used within RouterProvider");
537
- return context;
538
- };
539
- var useNavigationState = () => useServiceState(NavigationContext);
540
- var useRouterActions = () => useServiceState(ActionsContext);
541
- var useRouterData = () => useServiceState(DataContext);
542
- //#endregion
543
492
  //#region hooks/useApplyCustomAnimation.ts
544
493
  var useApplyCustomAnimation = (animationDuration) => {
545
494
  useEffect(() => {
@@ -579,8 +528,18 @@ var useApplyCustomAnimation = (animationDuration) => {
579
528
  //#endregion
580
529
  //#region hooks/usePreserveScroll.ts
581
530
  var usePreserveScroll = (preserveScroll) => {
582
- const { restoreScroll } = useRouterActions();
583
- const { routeItemData: { location: { pathname } } } = useNavigationState();
531
+ const [routeItemData] = useRouteItemData();
532
+ const [scrollMap] = useScrollMap();
533
+ const { pathname } = routeItemData.location;
534
+ const restoreScroll = useCallback(() => {
535
+ if (!pathname || !scrollMap[pathname]) return;
536
+ requestAnimationFrame(() => {
537
+ window.scrollTo({
538
+ top: scrollMap[pathname],
539
+ behavior: "smooth"
540
+ });
541
+ });
542
+ }, [pathname, scrollMap]);
584
543
  useEffect(() => {
585
544
  if (preserveScroll) restoreScroll();
586
545
  }, [
@@ -595,6 +554,14 @@ var useSetRouterConfig = (routerProps) => {
595
554
  useEffect(() => routerConfig.configure(routerProps), [routerProps]);
596
555
  };
597
556
  //#endregion
557
+ //#region hooks/useSetInitialContext.ts
558
+ var useSetInitialContext = (initialContext) => {
559
+ const [, setContext] = useContextState();
560
+ useEffect(() => {
561
+ if (initialContext) setContext(initialContext);
562
+ }, [initialContext, setContext]);
563
+ };
564
+ //#endregion
598
565
  //#region components/Spinner.tsx
599
566
  var Spinner = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cr-spinner" });
600
567
  //#endregion
@@ -606,8 +573,11 @@ var renderElement = (Component) => {
606
573
  //#endregion
607
574
  //#region components/Router.tsx
608
575
  var EmptyBoundary = ({ children }) => children;
609
- var Router = ({ isAnimated, animationDuration, spinner = true, preserveScroll = true, showFallbackOnAnimation = false, prefetch = "hover", hoverPrefetchDelay = 150, errorBoundary: ErrorBoundary = EmptyBoundary }) => {
610
- const { routeItemData: { routeItem, location }, loaderState, currentLoaderFallback, isLoading } = useNavigationState();
576
+ var Router = ({ isAnimated, animationDuration, spinner = true, preserveScroll = true, showFallbackOnAnimation = false, prefetch = "hover", hoverPrefetchDelay = 150, errorBoundary: ErrorBoundary = EmptyBoundary, context: initialContext }) => {
577
+ const [isLoading] = useIsLoading();
578
+ const [currentLoaderFallback] = useLoaderFallback();
579
+ const [routeItemData] = useRouteItemData();
580
+ const [loaderState] = useCurrentLoaderState();
611
581
  usePreserveScroll(preserveScroll);
612
582
  useApplyCustomAnimation(animationDuration);
613
583
  useSetRouterConfig({
@@ -616,9 +586,11 @@ var Router = ({ isAnimated, animationDuration, spinner = true, preserveScroll =
616
586
  prefetch,
617
587
  hoverPrefetchDelay
618
588
  });
589
+ useSetInitialContext(initialContext);
619
590
  const showErrorElement = !isLoading && Boolean(loaderState.loaderError || loaderState.beforeLoadError);
620
591
  const showSpinner = spinner && isAnimated && isLoading;
621
592
  const loadingContent = !showErrorElement && isLoading;
593
+ const { routeItem, location } = routeItemData;
622
594
  if ((showFallbackOnAnimation || !isAnimated) && loadingContent) return renderElement(currentLoaderFallback);
623
595
  if (!showFallbackOnAnimation && isAnimated && loadingContent) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {});
624
596
  if (!routeItem) return null;
@@ -629,22 +601,45 @@ var Router = ({ isAnimated, animationDuration, spinner = true, preserveScroll =
629
601
  });
630
602
  };
631
603
  //#endregion
604
+ //#region hooks/useServiceContext.ts
605
+ var useServiceState = (reactContext) => {
606
+ const context = useContext(reactContext);
607
+ if (!Object.keys(context).length) throw new Error("hooks and Router component must be used within RouterProvider");
608
+ return context;
609
+ };
610
+ var useRouterActions = () => useServiceState(ActionsContext);
611
+ //#endregion
632
612
  //#region hooks/useLocation.ts
633
613
  var useLocation = () => {
634
- const { routeItemData: { location } } = useNavigationState();
635
- return location;
614
+ const [routeItemData] = useRouteItemData();
615
+ return routeItemData.location;
636
616
  };
637
617
  //#endregion
638
618
  //#region hooks/useNavigate.ts
639
619
  var useNavigate = () => {
620
+ const [blockedRoute, setBlockedRoute] = useBlockedRoute();
640
621
  const { updateLocation } = useRouterActions();
641
622
  const locationRef = useLatest(useLocation());
623
+ const blockedRouteRef = useLatest(blockedRoute);
642
624
  return useCallback(async (arg) => {
643
- if (typeof arg === "number") return history.go(arg);
625
+ if (arg !== -1 && blockedRouteRef.current.from) {
626
+ const to = typeof arg === "object" ? arg.pathname : arg;
627
+ setBlockedRoute((prevState) => ({
628
+ ...prevState,
629
+ to
630
+ }));
631
+ return;
632
+ }
633
+ if (arg === -1) return history.go(arg);
644
634
  if (typeof arg === "string") {
645
635
  if (arg !== locationRef.current.pathname) await updateLocation({ pathname: arg });
646
636
  } else if (JSON.stringify(arg) !== JSON.stringify(locationRef.current)) await updateLocation(arg);
647
- }, [updateLocation, locationRef]);
637
+ }, [
638
+ blockedRouteRef,
639
+ locationRef,
640
+ setBlockedRoute,
641
+ updateLocation
642
+ ]);
648
643
  };
649
644
  //#endregion
650
645
  //#region components/Link.tsx
@@ -711,7 +706,8 @@ var Link = ({ children, to, prefetch: prefetchLink, hoverPrefetchDelay }) => {
711
706
  //#endregion
712
707
  //#region hooks/useParams.ts
713
708
  var useParams = () => {
714
- const { routeItemData: { routeItem, location: { pathname } } } = useNavigationState();
709
+ const [routeItemData] = useRouteItemData();
710
+ const { routeItem, location: { pathname } } = routeItemData;
715
711
  if (!routeItem) return void 0;
716
712
  return getParamsObject({
717
713
  params: routeItem?.params,
@@ -721,7 +717,7 @@ var useParams = () => {
721
717
  //#endregion
722
718
  //#region hooks/useLoaderState.ts
723
719
  var useLoaderState = () => {
724
- const { loaderState } = useNavigationState();
720
+ const [loaderState] = useCurrentLoaderState();
725
721
  return loaderState;
726
722
  };
727
723
  //#endregion
@@ -733,9 +729,32 @@ var useInvalidate = () => {
733
729
  //#endregion
734
730
  //#region hooks/useBlocker.ts
735
731
  var useBlocker = (blockerFn) => {
736
- const { blockerState } = useNavigationState();
737
- const { routeItemData: { location: { pathname } } } = useNavigationState();
738
- const { updateBlockedRoute } = useRouterActions();
732
+ const [blockedRoute, setBlockedRoute] = useBlockedRoute();
733
+ const { updateLocation } = useRouterActions();
734
+ const [routeItemData] = useRouteItemData();
735
+ const { location: { pathname } } = routeItemData;
736
+ const updateBlockedRoute = useCallback(({ type, payload = "" }) => setBlockedRoute((prevState) => {
737
+ if (prevState.from === payload && type === "charge") return prevState;
738
+ if (payload && prevState.from !== payload && type === "charge") return {
739
+ ...prevState,
740
+ from: payload
741
+ };
742
+ if (type === "reset") return {
743
+ ...prevState,
744
+ to: ""
745
+ };
746
+ if (type === "process") updateLocation({ pathname: prevState.to });
747
+ if (!prevState.from && !prevState.to) return prevState;
748
+ return {
749
+ from: "",
750
+ to: ""
751
+ };
752
+ }), [setBlockedRoute, updateLocation]);
753
+ const blockerState = useMemo(() => {
754
+ if (blockedRoute.from && blockedRoute.to) return "blocked";
755
+ if (blockedRoute.from) return "charged";
756
+ return "unblocked";
757
+ }, [blockedRoute]);
739
758
  const shouldBlock = blockerFn();
740
759
  useEffect(() => updateBlockedRoute(shouldBlock ? {
741
760
  type: "charge",
@@ -767,8 +786,7 @@ var useBeforeUnload = (callback) => {
767
786
  //#endregion
768
787
  //#region hooks/useRouterContext.ts
769
788
  var useRouterContext = () => {
770
- const { context } = useRouterData();
771
- const { setContext } = useRouterActions();
789
+ const [context, setContext] = useContextState();
772
790
  return {
773
791
  context,
774
792
  setContext
@@ -874,6 +892,12 @@ var useHistoricalTrail = () => {
874
892
  return trail;
875
893
  };
876
894
  //#endregion
895
+ //#region context/FormContext.ts
896
+ var FormContext = createContext({ isSubmitting: false });
897
+ //#endregion
898
+ //#region hooks/useFormContext.ts
899
+ var useFormContext = () => useContext(FormContext);
900
+ //#endregion
877
901
  //#region utils/adapter.ts
878
902
  var adapter = {
879
903
  string: { parse: (params) => params[0] || "" },
@@ -936,4 +960,4 @@ var adapter = {
936
960
  })
937
961
  };
938
962
  //#endregion
939
- export { Link, Router, RouterProvider, adapter, createRouter, useBeforeUnload, useBlocker, useHistoricalTrail, useInvalidate, useLoaderState, useLocation, useNavigate, useParams, useQueryParam, useRouterContext, useSearchParams };
963
+ export { Link, Router, RouterProvider, adapter, createRouter, useBeforeUnload, useBlocker, useFormContext, useHistoricalTrail, useInvalidate, useLoaderState, useLocation, useNavigate, useParams, useQueryParam, useRouterContext, useSearchParams };
@@ -0,0 +1,3 @@
1
+ import { PropsWithChildren } from 'react';
2
+ import { FormContextProps } from '../context/FormContext';
3
+ export declare const FormProvider: ({ children, isSubmitting }: PropsWithChildren<FormContextProps>) => import("react/jsx-runtime").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import { type ReactNode } from 'react';
2
- import { type ActionsContextValue, type DataContextValue, type NavigationContextValue } from '../context/RouterProviderContext';
3
- type ProviderProps = NavigationContextValue & ActionsContextValue & DataContextValue & {
2
+ import { type ActionsContextValue } from '../context/RouterProviderContext';
3
+ type ProviderProps = ActionsContextValue & {
4
4
  children: ReactNode;
5
5
  };
6
- export declare const Provider: ({ children, setContext, context, updateBlockedRoute, updateLocation, prefetchLoader, blockerState, routeItemData, restoreScroll, currentLoaderFallback, isLoading, loaderState, invalidate, }: ProviderProps) => import("react/jsx-runtime").JSX.Element;
6
+ export declare const Provider: ({ children, updateLocation, prefetchLoader, invalidate }: ProviderProps) => import("react/jsx-runtime").JSX.Element;
7
7
  export {};
@@ -0,0 +1,3 @@
1
+ type SetStateAction<T> = ((prevState: T) => T) | T;
2
+ export declare const createState: <T>(initialState: T) => () => readonly [T, (action: SetStateAction<T>) => void];
3
+ export {};
@@ -0,0 +1,20 @@
1
+ import { LoaderState, RouteItemData } from '../types/global';
2
+ export declare const useIsLoading: () => readonly [boolean, (action: boolean | ((prevState: boolean) => boolean)) => void];
3
+ export declare const useBlockedRoute: () => readonly [{
4
+ from: string;
5
+ to: string;
6
+ }, (action: {
7
+ from: string;
8
+ to: string;
9
+ } | ((prevState: {
10
+ from: string;
11
+ to: string;
12
+ }) => {
13
+ from: string;
14
+ to: string;
15
+ })) => void];
16
+ export declare const useLoaderFallback: () => readonly [(import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | (() => import("react").ReactElement)) | undefined, (action: (import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | (() => import("react").ReactElement)) | ((prevState: (import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | (() => import("react").ReactElement)) | undefined) => (import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | (() => import("react").ReactElement)) | undefined) | undefined) => void];
17
+ export declare const useRouteItemData: () => readonly [RouteItemData, (action: RouteItemData | ((prevState: RouteItemData) => RouteItemData)) => void];
18
+ export declare const useCurrentLoaderState: () => readonly [LoaderState, (action: LoaderState | ((prevState: LoaderState) => LoaderState)) => void];
19
+ export declare const useScrollMap: () => readonly [Record<string, number>, (action: Record<string, number> | ((prevState: Record<string, number>) => Record<string, number>)) => void];
20
+ export declare const useContextState: () => readonly [Record<string, unknown>, (action: Record<string, unknown> | ((prevState: Record<string, unknown>) => Record<string, unknown>)) => void];
@@ -27,6 +27,12 @@ export type ClientRouteItem = {
27
27
  params: Record<string, string>;
28
28
  setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
29
29
  }) => Promise<void>;
30
+ actions?: (arg: {
31
+ context: Record<string, unknown>;
32
+ params: Record<string, string>;
33
+ invalidate: (path?: string) => Promise<void>;
34
+ setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
35
+ }) => Record<string, (arg: FormData) => Promise<unknown> | Promise<void> | void | unknown>;
30
36
  };
31
37
  export type RouteItem = ClientRouteItem & {
32
38
  element: Element;
@@ -42,10 +48,6 @@ export type Location = {
42
48
  state?: unknown;
43
49
  };
44
50
  export type BlockerState = 'blocked' | 'unblocked' | 'charged';
45
- export type UpdateBlockedRouteProps = {
46
- type: 'process' | 'reset' | 'charge' | 'unblock';
47
- payload?: string;
48
- };
49
51
  export type RevalidateCacheArgs = {
50
52
  pathname: string;
51
53
  routeItem?: RouteItem;
@@ -74,5 +76,6 @@ export type RouterProps = {
74
76
  errorBoundary?: ComponentType<{
75
77
  children: ReactNode;
76
78
  }>;
79
+ context?: Record<string, unknown>;
77
80
  };
78
81
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clear-react-router",
3
- "version": "1.6.4",
3
+ "version": "1.6.7",
4
4
  "description": "A lightweight, type-safe routing library for React applications",
5
5
  "author": "Andrew Bubnov",
6
6
  "scripts": {
@@ -1,26 +0,0 @@
1
- import { type Dispatch, RefObject, type SetStateAction } from 'react';
2
- import { BlockerState, LoaderState, Location, RevalidateCacheArgs, RouteItem, RouteItemData, UpdateBlockedRouteProps } from '../types/global';
3
- type UseHandleNavigation = {
4
- routes: RouteItem[];
5
- context: Record<string, unknown>;
6
- revalidateCache(arg: RevalidateCacheArgs): Promise<unknown>;
7
- setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
8
- isCacheItemFresh(arg: {
9
- routeItem?: RouteItem;
10
- pathname: string;
11
- }): boolean;
12
- loaderStateRef: RefObject<LoaderState>;
13
- clearTimestamp(path: string): void;
14
- };
15
- export declare const useHandleNavigation: ({ routes, context, revalidateCache, setContext, isCacheItemFresh, loaderStateRef, clearTimestamp, }: UseHandleNavigation) => {
16
- blockerState: BlockerState;
17
- updateLocation: (nextLocation: Location) => Promise<void>;
18
- updateBlockedRoute: ({ type, payload }: UpdateBlockedRouteProps) => void;
19
- routeItemData: RouteItemData;
20
- restoreScroll: () => void;
21
- currentLoaderFallback: (import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | (() => import("react").ReactElement)) | undefined;
22
- isLoading: boolean;
23
- loaderState: LoaderState;
24
- invalidate: (pathname?: string) => Promise<void>;
25
- };
26
- export {};