clear-react-router 1.4.4 → 1.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@ type RouterProps = {
2
2
  isAnimated?: boolean;
3
3
  animationDuration?: number;
4
4
  spinner?: boolean;
5
+ preserveScroll?: boolean;
5
6
  };
6
- export declare const Router: ({ isAnimated, animationDuration, spinner }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
7
+ export declare const Router: ({ isAnimated, animationDuration, spinner, preserveScroll }: RouterProps) => import("react/jsx-runtime").JSX.Element | null;
7
8
  export {};
@@ -4,7 +4,6 @@ type RouteProviderProps = {
4
4
  children: ReactNode;
5
5
  routeList: RouteItem[];
6
6
  context?: Record<string, unknown>;
7
- preserveScroll?: boolean;
8
7
  };
9
- export declare const RouterProvider: ({ children, routeList, context: initialContext, preserveScroll, }: RouteProviderProps) => import("react/jsx-runtime").JSX.Element;
8
+ export declare const RouterProvider: ({ children, routeList, context: initialContext }: RouteProviderProps) => import("react/jsx-runtime").JSX.Element;
10
9
  export {};
@@ -1,5 +1,5 @@
1
1
  import { Dispatch, SetStateAction } from 'react';
2
- import { BlockerState, LoaderState, Location, RouteItem, UpdateBlockedRouteProps } from '../types/global';
2
+ import { BlockerState, LoaderState, Location, NextItemData, RouteItem, UpdateBlockedRouteProps } from '../types/global';
3
3
  export type PropsContextValue = {
4
4
  routeList: RouteItem[];
5
5
  };
@@ -7,7 +7,7 @@ export type NavigationContextValue = {
7
7
  location: Location;
8
8
  blockerState: BlockerState;
9
9
  isLoading: boolean;
10
- nextRouteItem: RouteItem | undefined;
10
+ nextItemData: NextItemData;
11
11
  };
12
12
  export type ActionsContextValue = {
13
13
  setLocation: Dispatch<SetStateAction<Location>>;
@@ -1,18 +1,17 @@
1
1
  import { type Dispatch, type SetStateAction } from 'react';
2
- import type { BlockerState, LoaderState, Location, RevalidateCacheArgs, RouteItem, UpdateBlockedRouteProps } from '../types/global';
2
+ import { BlockerState, LoaderState, Location, NextItemData, RevalidateCacheArgs, RouteItem, UpdateBlockedRouteProps } from '../types/global';
3
3
  type UseHandleNavigation = {
4
4
  routeList: RouteItem[];
5
5
  setLocation: (arg: Location) => void;
6
6
  context: Record<string, unknown>;
7
7
  revalidateCache(arg: RevalidateCacheArgs): Promise<void>;
8
8
  setContext: Dispatch<SetStateAction<Record<string, unknown>>>;
9
- setScrollMap: Dispatch<SetStateAction<Record<string, number>>>;
10
9
  setLoaderState: Dispatch<SetStateAction<LoaderState>>;
11
10
  };
12
- export declare const useHandleNavigation: ({ setLocation, routeList, context, revalidateCache, setContext, setScrollMap, setLoaderState, }: UseHandleNavigation) => {
11
+ export declare const useHandleNavigation: ({ setLocation, routeList, context, revalidateCache, setContext, setLoaderState, }: UseHandleNavigation) => {
13
12
  blockerState: BlockerState;
14
13
  updateLocation: (nextLocation: Location) => Promise<void>;
15
14
  updateBlockedRoute: ({ type, payload }: UpdateBlockedRouteProps) => void;
16
- nextRouteItem: RouteItem | undefined;
15
+ nextItemData: NextItemData;
17
16
  };
18
17
  export {};
@@ -1,6 +1,7 @@
1
1
  type UsePreserveScrollParams = {
2
2
  pathname: string;
3
3
  preserveScroll: boolean;
4
+ nextPathname?: string;
4
5
  };
5
- export declare const usePreserveScroll: ({ pathname, preserveScroll }: UsePreserveScrollParams) => import("react").Dispatch<import("react").SetStateAction<Record<string, number>>>;
6
+ export declare const usePreserveScroll: ({ pathname, preserveScroll, nextPathname }: UsePreserveScrollParams) => void;
6
7
  export {};
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ 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, location, setLocation, prefetchLoader, loaderState, blockerState, routeList, isLoading, nextRouteItem }) => {
48
+ var Provider = ({ children, setContext, context, updateBlockedRoute, updateLocation, location, setLocation, prefetchLoader, loaderState, blockerState, routeList, isLoading, nextItemData }) => {
49
49
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PropsContext.Provider, {
50
50
  value: { routeList },
51
51
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActionsContext.Provider, {
@@ -66,7 +66,7 @@ var Provider = ({ children, setContext, context, updateBlockedRoute, updateLocat
66
66
  blockerState,
67
67
  location,
68
68
  isLoading,
69
- nextRouteItem
69
+ nextItemData
70
70
  },
71
71
  children
72
72
  })
@@ -113,10 +113,10 @@ var parseClientRouteItem = (el, parentParams = [], parentPath = "") => {
113
113
  }, ...el.children?.flatMap((child) => parseClientRouteItem(child, currentParams, path)) || []];
114
114
  };
115
115
  var createRouter = (clientList) => clientList.flatMap((el) => parseClientRouteItem(el, []));
116
- var getParamsObject = ({ routeItem, pathname }) => {
117
- if (!routeItem?.params) return {};
116
+ var getParamsObject = ({ params, pathname }) => {
117
+ if (!params) return {};
118
118
  const split = pathname.split("/");
119
- return (routeItem.params || []).map((el) => ({
119
+ return (params || []).map((el) => ({
120
120
  index: split.findIndex((item) => item === el.key),
121
121
  value: el.value
122
122
  })).reduce((acc, cur) => ({
@@ -136,22 +136,14 @@ var comparePaths = (el, pathname) => {
136
136
  };
137
137
  //#endregion
138
138
  //#region hooks/useHandleNavigation.ts
139
- var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, setContext, setScrollMap, setLoaderState }) => {
139
+ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, setContext, setLoaderState }) => {
140
140
  const [blockedRoute, setBlockedRoute] = useState({
141
141
  from: "",
142
142
  to: ""
143
143
  });
144
- const [nextRouteItem, setNextRouteItem] = useState();
144
+ const [nextItemData, setNextItemData] = useState({});
145
145
  const prevPathname = useRef("");
146
146
  const navigationSeq = useRef(0);
147
- const updateScrollMap = useCallback(() => setScrollMap((prevState) => {
148
- const scrollPosition = document.scrollingElement?.scrollTop ?? 0;
149
- if (!scrollPosition || prevState[prevPathname.current] === scrollPosition) return prevState;
150
- return {
151
- ...prevState,
152
- [prevPathname.current]: scrollPosition
153
- };
154
- }), [setScrollMap]);
155
147
  const navigation = useCallback((nextLocation) => {
156
148
  setLocation(nextLocation);
157
149
  prevPathname.current = nextLocation.pathname;
@@ -169,11 +161,14 @@ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, s
169
161
  const navigationHandler = useCallback(async (nextLocation) => {
170
162
  navigationSeq.current = navigationSeq.current + 1;
171
163
  const seq = navigationSeq.current;
172
- updateScrollMap();
173
164
  const nextItem = routeList.find((el) => comparePaths(el, nextLocation.pathname));
174
- setNextRouteItem(nextItem);
165
+ setNextItemData({
166
+ loaderFallback: nextItem?.loaderFallback,
167
+ params: nextItem?.params,
168
+ pathname: nextLocation.pathname
169
+ });
175
170
  const params = getParamsObject({
176
- routeItem: nextItem,
171
+ params: nextItem?.params,
177
172
  pathname: nextLocation.pathname
178
173
  });
179
174
  if (nextItem?.beforeLoad) try {
@@ -221,8 +216,7 @@ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, s
221
216
  routeList,
222
217
  transitionedNavigation,
223
218
  setContext,
224
- setLoaderState,
225
- updateScrollMap
219
+ setLoaderState
226
220
  ]);
227
221
  const setNextLocationRef = useLatest(navigationHandler);
228
222
  const updateBlockedRoute = useCallback(({ type, payload = "" }) => setBlockedRoute((prevState) => {
@@ -276,7 +270,7 @@ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, s
276
270
  }, [blockedRoute]),
277
271
  updateLocation,
278
272
  updateBlockedRoute,
279
- nextRouteItem
273
+ nextItemData
280
274
  };
281
275
  };
282
276
  //#endregion
@@ -300,7 +294,7 @@ var useLoader = ({ routeList, context, setContext, setLoaderState }) => {
300
294
  if (isCurrentRoute) setIsLoading(true);
301
295
  try {
302
296
  const params = getParamsObject({
303
- routeItem,
297
+ params: routeItem.params,
304
298
  pathname
305
299
  });
306
300
  const result = await routeItem?.loader({
@@ -351,45 +345,23 @@ var useLoader = ({ routeList, context, setContext, setLoaderState }) => {
351
345
  };
352
346
  };
353
347
  //#endregion
354
- //#region hooks/usePreserveScroll.ts
355
- var usePreserveScroll = ({ pathname, preserveScroll }) => {
356
- const [scrollMap, setScrollMap] = useState({});
357
- useEffect(() => {
358
- if (!preserveScroll || !pathname || !scrollMap[pathname]) return;
359
- document.scrollingElement?.scrollTo({
360
- top: scrollMap[pathname],
361
- behavior: "smooth"
362
- });
363
- }, [
364
- pathname,
365
- scrollMap,
366
- preserveScroll
367
- ]);
368
- return setScrollMap;
369
- };
370
- //#endregion
371
348
  //#region components/RouterProvider.tsx
372
- var RouterProvider = ({ children, routeList, context: initialContext = {}, preserveScroll = true }) => {
349
+ var RouterProvider = ({ children, routeList, context: initialContext = {} }) => {
373
350
  const [location, setLocation] = useState({});
374
351
  const [context, setContext] = useState(initialContext);
375
352
  const [loaderState, setLoaderState] = useState({});
376
- const setScrollMap = usePreserveScroll({
377
- pathname: location.pathname,
378
- preserveScroll
379
- });
380
353
  const { prefetchLoader, revalidateCache, isLoading } = useLoader({
381
354
  routeList,
382
355
  context,
383
356
  setContext,
384
357
  setLoaderState
385
358
  });
386
- const { blockerState, updateLocation, updateBlockedRoute, nextRouteItem } = useHandleNavigation({
359
+ const { blockerState, updateLocation, updateBlockedRoute, nextItemData } = useHandleNavigation({
387
360
  setLocation,
388
361
  routeList,
389
362
  context,
390
363
  setContext,
391
364
  revalidateCache,
392
- setScrollMap,
393
365
  setLoaderState
394
366
  });
395
367
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Provider, {
@@ -404,7 +376,7 @@ var RouterProvider = ({ children, routeList, context: initialContext = {}, prese
404
376
  context,
405
377
  setContext,
406
378
  routeList,
407
- nextRouteItem,
379
+ nextItemData,
408
380
  isLoading
409
381
  }), [
410
382
  blockerState,
@@ -416,7 +388,7 @@ var RouterProvider = ({ children, routeList, context: initialContext = {}, prese
416
388
  routeList,
417
389
  updateBlockedRoute,
418
390
  updateLocation,
419
- nextRouteItem
391
+ nextItemData
420
392
  ]),
421
393
  children
422
394
  });
@@ -470,6 +442,36 @@ var useApplyCustomAnimation = (animationDuration) => {
470
442
  }, [animationDuration]);
471
443
  };
472
444
  //#endregion
445
+ //#region hooks/usePreserveScroll.ts
446
+ var usePreserveScroll = ({ pathname, preserveScroll, nextPathname }) => {
447
+ const [scrollMap, setScrollMap] = useState({});
448
+ const prevPathname = useRef("");
449
+ useEffect(() => {
450
+ setScrollMap((prevState) => {
451
+ const scrollPosition = document.scrollingElement?.scrollTop ?? 0;
452
+ if (!scrollPosition || prevState[prevPathname.current] === scrollPosition) return prevState;
453
+ return {
454
+ ...prevState,
455
+ [prevPathname.current]: scrollPosition
456
+ };
457
+ });
458
+ prevPathname.current = pathname;
459
+ }, [pathname, nextPathname]);
460
+ useEffect(() => {
461
+ if (!preserveScroll || !pathname || !scrollMap[pathname]) return;
462
+ requestAnimationFrame(() => {
463
+ window.scrollTo({
464
+ top: scrollMap[pathname],
465
+ behavior: "smooth"
466
+ });
467
+ });
468
+ }, [
469
+ pathname,
470
+ scrollMap,
471
+ preserveScroll
472
+ ]);
473
+ };
474
+ //#endregion
473
475
  //#region context/RouterViewContext.ts
474
476
  var RouterViewContext = createContext({});
475
477
  //#endregion
@@ -492,34 +494,37 @@ var renderElement = (Component) => {
492
494
  //#endregion
493
495
  //#region components/Router.tsx
494
496
  var ALL_LOCATIONS = "*";
495
- var Router = ({ isAnimated, animationDuration, spinner = true }) => {
496
- const { location, isLoading, nextRouteItem } = useNavigationState();
497
+ var Router = ({ isAnimated, animationDuration, spinner = true, preserveScroll = true }) => {
498
+ const { location, isLoading, nextItemData } = useNavigationState();
497
499
  const { routeList } = usePropsData();
498
500
  const { loaderState } = useRouterData();
501
+ usePreserveScroll({
502
+ nextPathname: nextItemData.pathname,
503
+ pathname: location.pathname,
504
+ preserveScroll
505
+ });
499
506
  useApplyCustomAnimation(animationDuration);
500
507
  const routeItem = useMemo(() => {
501
508
  if (!location.pathname) return void 0;
502
509
  return routeList.find((el) => el.path === ALL_LOCATIONS || comparePaths(el, location.pathname));
503
510
  }, [location.pathname, routeList]);
504
- const params = useMemo(() => {
505
- return getParamsObject({
506
- routeItem: routeItem || nextRouteItem,
507
- pathname: location.pathname || window.location.pathname
508
- });
509
- }, [
511
+ const params = useMemo(() => getParamsObject({
512
+ params: nextItemData.params,
513
+ pathname: nextItemData.pathname || location.pathname
514
+ }), [
510
515
  location.pathname,
511
- routeItem,
512
- nextRouteItem
516
+ nextItemData.params,
517
+ nextItemData.pathname
513
518
  ]);
514
519
  const shouldErrorElementShown = useMemo(() => Boolean(loaderState[location.pathname]?.loaderError || loaderState[location.pathname]?.beforeLoadError), [loaderState, location.pathname]);
515
- if (!routeItem && !nextRouteItem) return null;
516
- if (!routeItem && nextRouteItem) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ViewProvider, {
520
+ if (!routeItem && !nextItemData.loaderFallback) return null;
521
+ if (!routeItem && nextItemData.loaderFallback) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ViewProvider, {
517
522
  params,
518
- children: renderElement(nextRouteItem?.loaderFallback)
523
+ children: renderElement(nextItemData.loaderFallback)
519
524
  });
520
- if (!isAnimated && !shouldErrorElementShown && isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ViewProvider, {
525
+ if (!isAnimated && !shouldErrorElementShown && isLoading && nextItemData.loaderFallback) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ViewProvider, {
521
526
  params,
522
- children: renderElement(nextRouteItem?.loaderFallback)
527
+ children: renderElement(nextItemData.loaderFallback)
523
528
  });
524
529
  if (shouldErrorElementShown) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ViewProvider, {
525
530
  params,
@@ -3,5 +3,5 @@ import { type ActionsContextValue, type DataContextValue, type NavigationContext
3
3
  type ProviderProps = PropsContextValue & NavigationContextValue & ActionsContextValue & DataContextValue & {
4
4
  children: ReactNode;
5
5
  };
6
- export declare const Provider: ({ children, setContext, context, updateBlockedRoute, updateLocation, location, setLocation, prefetchLoader, loaderState, blockerState, routeList, isLoading, nextRouteItem, }: ProviderProps) => import("react/jsx-runtime").JSX.Element;
6
+ export declare const Provider: ({ children, setContext, context, updateBlockedRoute, updateLocation, location, setLocation, prefetchLoader, loaderState, blockerState, routeList, isLoading, nextItemData, }: ProviderProps) => import("react/jsx-runtime").JSX.Element;
7
7
  export {};
@@ -59,3 +59,8 @@ export type Adapter<T> = {
59
59
  parse: (params: string[]) => T;
60
60
  serialize?: (params: T) => string | string[];
61
61
  };
62
+ export type NextItemData = {
63
+ loaderFallback: RouteItem['loaderFallback'];
64
+ params: RouteItem['params'];
65
+ pathname: string;
66
+ };
@@ -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: ({ routeItem, pathname }: {
4
- routeItem?: RouteItem;
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,6 +1,6 @@
1
1
  {
2
2
  "name": "clear-react-router",
3
- "version": "1.4.4",
3
+ "version": "1.4.6",
4
4
  "description": "A lightweight, type-safe routing library for React applications",
5
5
  "author": "Andrew Bubnov",
6
6
  "scripts": {