@aminnairi/react-router 1.0.1 → 2.0.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 +120 -138
- package/index.tsx +101 -16
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ export const Fallback = () => {
|
|
|
60
60
|
const navigateToHomePage = useNavigateToPage(home);
|
|
61
61
|
|
|
62
62
|
return (
|
|
63
|
-
<button onClick={navigateToHomePage}>
|
|
63
|
+
<button onClick={() => navigateToHomePage({})}>
|
|
64
64
|
Go back home
|
|
65
65
|
</button>
|
|
66
66
|
);
|
|
@@ -82,7 +82,7 @@ export const Issue = () => {
|
|
|
82
82
|
return (
|
|
83
83
|
<Fragment>
|
|
84
84
|
<h1>An issue occurred</h1>
|
|
85
|
-
<button onClick={
|
|
85
|
+
<button onClick={() => navigateToHomePage({})}>
|
|
86
86
|
Go back home
|
|
87
87
|
</button>
|
|
88
88
|
</Fragment>
|
|
@@ -94,11 +94,11 @@ export const Issue = () => {
|
|
|
94
94
|
touch src/router/index.ts
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
```
|
|
97
|
+
```ts
|
|
98
98
|
import { createRouter } from "@aminnairi/react-router";
|
|
99
|
-
import { Fallback } from "./
|
|
100
|
-
import { Issue } from "./
|
|
101
|
-
import { home } from "./
|
|
99
|
+
import { Fallback } from "./fallback";
|
|
100
|
+
import { Issue } from "./issue";
|
|
101
|
+
import { home } from "./pages/home";
|
|
102
102
|
|
|
103
103
|
export const router = createRouter({
|
|
104
104
|
fallback: Fallback,
|
|
@@ -259,7 +259,7 @@ const about = createPage({
|
|
|
259
259
|
<h1>
|
|
260
260
|
About Us
|
|
261
261
|
</h1>
|
|
262
|
-
<button onClick={navigateToLoginPage}>
|
|
262
|
+
<button onClick={() => navigateToLoginPage({})}>
|
|
263
263
|
Login
|
|
264
264
|
</button>
|
|
265
265
|
</Fragment>
|
|
@@ -277,7 +277,7 @@ createPage({
|
|
|
277
277
|
<h1>
|
|
278
278
|
Home
|
|
279
279
|
</h1>
|
|
280
|
-
<button onClick={navigateToAboutPage}>
|
|
280
|
+
<button onClick={() => navigateToAboutPage({})}>
|
|
281
281
|
About Us
|
|
282
282
|
</button>
|
|
283
283
|
</Fragment>
|
|
@@ -385,12 +385,14 @@ root.render(
|
|
|
385
385
|
|
|
386
386
|
You can also activate the View Transition Web API if you want before each page renders. This is nice because by default, the browser already has some styling that allows for a smooth and simple transition between pages.
|
|
387
387
|
|
|
388
|
-
All you have to do is to
|
|
388
|
+
All you have to do is to provide a `transition` function in the arguments of the `createRouter` function. This function receives the navigation direction (`"pushstate"` or `"popstate"`) and a `next` callback to render the next page.
|
|
389
|
+
|
|
390
|
+
This library also exports a `slideFadeTransition` that you can use out-of-the-box.
|
|
389
391
|
|
|
390
392
|
```tsx
|
|
391
393
|
import { Fragment, StrictMode } from "react";
|
|
392
394
|
import { createRoot } from "react-dom/client";
|
|
393
|
-
import { createRouter, createPage } from "@aminnairi/react-router";
|
|
395
|
+
import { createRouter, createPage, slideFadeTransition } from "@aminnairi/react-router";
|
|
394
396
|
|
|
395
397
|
const home = createPage({
|
|
396
398
|
path: "/",
|
|
@@ -402,7 +404,7 @@ const home = createPage({
|
|
|
402
404
|
});
|
|
403
405
|
|
|
404
406
|
const router = createRouter({
|
|
405
|
-
transition:
|
|
407
|
+
transition: slideFadeTransition,
|
|
406
408
|
fallback: () => (
|
|
407
409
|
<h1>Not found</h1>
|
|
408
410
|
),
|
|
@@ -464,18 +466,15 @@ const home = createPage({
|
|
|
464
466
|
});
|
|
465
467
|
|
|
466
468
|
const router = createRouter({
|
|
467
|
-
transition: true,
|
|
468
469
|
fallback: () => (
|
|
469
470
|
<h1>Not found</h1>
|
|
470
471
|
),
|
|
471
472
|
issue: ({ error, reset }) => (
|
|
472
|
-
|
|
473
|
-
<
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
</Fragment>
|
|
478
|
-
);
|
|
473
|
+
<Fragment>
|
|
474
|
+
<h1>Error</h1>
|
|
475
|
+
<p>{error.message}</p>
|
|
476
|
+
<button onClick={reset}>Reset</button>
|
|
477
|
+
</Fragment>
|
|
479
478
|
),
|
|
480
479
|
pages: [
|
|
481
480
|
home
|
|
@@ -538,17 +537,14 @@ const Fallback = () => {
|
|
|
538
537
|
}
|
|
539
538
|
|
|
540
539
|
const Issue = createIssue(({ error, reset }) => (
|
|
541
|
-
|
|
542
|
-
<
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
</Fragment>
|
|
547
|
-
);
|
|
540
|
+
<Fragment>
|
|
541
|
+
<h1>Error</h1>
|
|
542
|
+
<p>{error.message}</p>
|
|
543
|
+
<button onClick={reset}>Reset</button>
|
|
544
|
+
</Fragment>
|
|
548
545
|
));
|
|
549
546
|
|
|
550
547
|
const router = createRouter({
|
|
551
|
-
transition: true,
|
|
552
548
|
fallback: Fallback,
|
|
553
549
|
issue: Issue,
|
|
554
550
|
pages: [
|
|
@@ -582,7 +578,9 @@ const App = () => {
|
|
|
582
578
|
|
|
583
579
|
root.render(
|
|
584
580
|
<StrictMode>
|
|
585
|
-
<
|
|
581
|
+
<router.Provider>
|
|
582
|
+
<App />
|
|
583
|
+
</router.Provider>
|
|
586
584
|
</StrictMode>
|
|
587
585
|
);
|
|
588
586
|
```
|
|
@@ -611,7 +609,7 @@ const Fallback = () => {
|
|
|
611
609
|
return (
|
|
612
610
|
<Fragment>
|
|
613
611
|
<h1>Not found</h1>
|
|
614
|
-
<button onClick={navigateToHomePage}>
|
|
612
|
+
<button onClick={() => navigateToHomePage({})}>
|
|
615
613
|
Go Back Home
|
|
616
614
|
</button>
|
|
617
615
|
</Fragment>
|
|
@@ -619,18 +617,15 @@ const Fallback = () => {
|
|
|
619
617
|
}
|
|
620
618
|
|
|
621
619
|
const Issue = createIssue(({ error, reset }) => (
|
|
622
|
-
|
|
623
|
-
<
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
</Fragment>
|
|
628
|
-
);
|
|
620
|
+
<Fragment>
|
|
621
|
+
<h1>Error</h1>
|
|
622
|
+
<p>{error.message}</p>
|
|
623
|
+
<button onClick={reset}>Reset</button>
|
|
624
|
+
</Fragment>
|
|
629
625
|
));
|
|
630
626
|
|
|
631
627
|
const router = createRouter({
|
|
632
628
|
prefix: "/portfolio",
|
|
633
|
-
transition: true,
|
|
634
629
|
fallback: Fallback,
|
|
635
630
|
issue: Issue,
|
|
636
631
|
pages: [
|
|
@@ -664,7 +659,9 @@ const App = () => {
|
|
|
664
659
|
|
|
665
660
|
root.render(
|
|
666
661
|
<StrictMode>
|
|
667
|
-
<
|
|
662
|
+
<router.Provider>
|
|
663
|
+
<App />
|
|
664
|
+
</router.Provider>
|
|
668
665
|
</StrictMode>
|
|
669
666
|
);
|
|
670
667
|
```
|
|
@@ -679,7 +676,7 @@ It accepts a page that has been created using `createPage`.
|
|
|
679
676
|
import { Fragment } from "react";
|
|
680
677
|
import { createPage, useNavigateToPage } from "@aminnairi/react-router";
|
|
681
678
|
|
|
682
|
-
const home =
|
|
679
|
+
const home = createPage({
|
|
683
680
|
path: "/",
|
|
684
681
|
element: function Home() {
|
|
685
682
|
return (
|
|
@@ -696,7 +693,7 @@ createPage({
|
|
|
696
693
|
return (
|
|
697
694
|
<Fragment>
|
|
698
695
|
<h1>About</h1>
|
|
699
|
-
<button onClick={navigateToHomePage}>Home</button>
|
|
696
|
+
<button onClick={() => navigateToHomePage({})}>Home</button>
|
|
700
697
|
</Fragment>
|
|
701
698
|
);
|
|
702
699
|
}
|
|
@@ -711,7 +708,7 @@ The parameters should always be provided as string, as they are the only data ty
|
|
|
711
708
|
import { Fragment } from "react";
|
|
712
709
|
import { createPage, useNavigateToPage } from "@aminnairi/react-router";
|
|
713
710
|
|
|
714
|
-
const user =
|
|
711
|
+
const user = createPage({
|
|
715
712
|
path: "/users/:user",
|
|
716
713
|
element: function User({ parameters: { user }}) {
|
|
717
714
|
return (
|
|
@@ -735,6 +732,40 @@ createPage({
|
|
|
735
732
|
});
|
|
736
733
|
```
|
|
737
734
|
|
|
735
|
+
### useLink
|
|
736
|
+
|
|
737
|
+
Allow you to navigate to another page using a JSX component instead of a callback as for the `useNavigateToPage` hook.
|
|
738
|
+
|
|
739
|
+
The created component is simply a `<a href="...">{children}</a>` under the hood which prevents the default behavior of the navigator which is to create a new HTTP request and to reload the page. The `href` attribute is computed from the page path and its parameters.
|
|
740
|
+
|
|
741
|
+
```tsx
|
|
742
|
+
import { Fragment } from "react";
|
|
743
|
+
import { createPage, useLink } from "@aminnairi/react-router";
|
|
744
|
+
|
|
745
|
+
const user = createPage({
|
|
746
|
+
path: "/users/:user",
|
|
747
|
+
element: function User({ parameters: { user }}) {
|
|
748
|
+
return (
|
|
749
|
+
<h1>User#{user}</h1>
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
createPage({
|
|
755
|
+
path: "/about",
|
|
756
|
+
element: function About() {
|
|
757
|
+
const Link = useLink(user);
|
|
758
|
+
|
|
759
|
+
return (
|
|
760
|
+
<Fragment>
|
|
761
|
+
<h1>About</h1>
|
|
762
|
+
<Link parameters={{ user: "123" }}>User#123</Link>
|
|
763
|
+
</Fragment>
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
```
|
|
768
|
+
|
|
738
769
|
### useSearch
|
|
739
770
|
|
|
740
771
|
Allow you to get one or more search query from the URL.
|
|
@@ -748,7 +779,7 @@ import { createPage, useSearch } from "@aminnairi/react-router";
|
|
|
748
779
|
createPage({
|
|
749
780
|
path: "/users",
|
|
750
781
|
element: function Home() {
|
|
751
|
-
const
|
|
782
|
+
const search = useSearch();
|
|
752
783
|
const sortedByDate = useMemo(() => search.get("sort-by") === "date", [search]);
|
|
753
784
|
|
|
754
785
|
return (
|
|
@@ -791,13 +822,13 @@ This function is mainly used in the internals of the `createRouter` and in most
|
|
|
791
822
|
```typescript
|
|
792
823
|
import { doesRouteMatchPath } from "@aminnairi/react-router";
|
|
793
824
|
|
|
794
|
-
|
|
825
|
+
doesRouteMatchPath("/", "/"); // true
|
|
795
826
|
|
|
796
|
-
|
|
827
|
+
doesRouteMatchPath("/", "/about"); // false
|
|
797
828
|
|
|
798
|
-
|
|
829
|
+
doesRouteMatchPath("/users/:user", "/users/123"); // true
|
|
799
830
|
|
|
800
|
-
|
|
831
|
+
doesRouteMatchPath("/users/:user", "/users/123/articles"); // false
|
|
801
832
|
```
|
|
802
833
|
|
|
803
834
|
You can also optionally provide a prefix.
|
|
@@ -805,13 +836,13 @@ You can also optionally provide a prefix.
|
|
|
805
836
|
```typescript
|
|
806
837
|
import { doesRouteMatchPath } from "@aminnairi/react-router";
|
|
807
838
|
|
|
808
|
-
|
|
839
|
+
doesRouteMatchPath("/", "/github", "/github"); // true
|
|
809
840
|
|
|
810
|
-
|
|
841
|
+
doesRouteMatchPath("/", "/github/about", "/github"); // false
|
|
811
842
|
|
|
812
|
-
|
|
843
|
+
doesRouteMatchPath("/users/:user", "/github/users/123", "/github"); // true
|
|
813
844
|
|
|
814
|
-
|
|
845
|
+
doesRouteMatchPath("/users/:user", "/github/users/123/articles", "/github"); // false
|
|
815
846
|
```
|
|
816
847
|
|
|
817
848
|
### getParameters
|
|
@@ -823,13 +854,13 @@ This function is mainly used in the internals of the `createRouter` and in most
|
|
|
823
854
|
```typescript
|
|
824
855
|
import { getParameters } from "@aminnairi/react-router";
|
|
825
856
|
|
|
826
|
-
getParameters("/", "/"); //
|
|
857
|
+
getParameters("/", "/"); // {}
|
|
827
858
|
|
|
828
|
-
getParameters("/", "/about"); //
|
|
859
|
+
getParameters("/", "/about"); // {}
|
|
829
860
|
|
|
830
861
|
getParameters("/users/:user", "/users/123"); // { user: "123" }
|
|
831
862
|
|
|
832
|
-
getParameters("/users/:user", "/users/123/articles"); // {
|
|
863
|
+
getParameters("/users/:user", "/users/123/articles"); // {}
|
|
833
864
|
```
|
|
834
865
|
|
|
835
866
|
You can also provide an optional prefix.
|
|
@@ -837,91 +868,13 @@ You can also provide an optional prefix.
|
|
|
837
868
|
```typescript
|
|
838
869
|
import { getParameters } from "@aminnairi/react-router";
|
|
839
870
|
|
|
840
|
-
getParameters("/", "/github", "/github"); //
|
|
871
|
+
getParameters("/", "/github", "/github"); // {}
|
|
841
872
|
|
|
842
|
-
getParameters("/", "/github/about", "/github"); //
|
|
873
|
+
getParameters("/", "/github/about", "/github"); // {}
|
|
843
874
|
|
|
844
875
|
getParameters("/users/:user", "/github/users/123", "/github"); // { user: "123" }
|
|
845
876
|
|
|
846
|
-
getParameters("/users/:user", "/github/users/123/articles", "/github"); // {
|
|
847
|
-
```
|
|
848
|
-
|
|
849
|
-
### findPage
|
|
850
|
-
|
|
851
|
-
Return a page that matches the `window.location.pathname` property containing the current URI of the page from an array of pages.
|
|
852
|
-
|
|
853
|
-
If it does not match any pages, it returns `undefined` instead.
|
|
854
|
-
|
|
855
|
-
This function is mainly used in the internals of the `createRouter` and in most case should not be necessary.
|
|
856
|
-
|
|
857
|
-
```tsx
|
|
858
|
-
import { findPage, createPage } from "@aminnairi/react-router";
|
|
859
|
-
|
|
860
|
-
const home = createPage({
|
|
861
|
-
path: "/",
|
|
862
|
-
element: () => <h1>Home</h1>
|
|
863
|
-
});
|
|
864
|
-
|
|
865
|
-
const about = createPage({
|
|
866
|
-
path: "/about",
|
|
867
|
-
element: () => <h1>About</h1>
|
|
868
|
-
});
|
|
869
|
-
|
|
870
|
-
const login = createPage({
|
|
871
|
-
path: "/login",
|
|
872
|
-
element: () => <h1>Login</h1>
|
|
873
|
-
});
|
|
874
|
-
|
|
875
|
-
const pages = [
|
|
876
|
-
home.page,
|
|
877
|
-
about.page,
|
|
878
|
-
login.page
|
|
879
|
-
];
|
|
880
|
-
|
|
881
|
-
const foundPage = findPage(pages, "/login");
|
|
882
|
-
|
|
883
|
-
if (foundPage) {
|
|
884
|
-
console.log("Found a page matching the current location");
|
|
885
|
-
console.log(foundPage.path);
|
|
886
|
-
} else {
|
|
887
|
-
console.log("No page matching the current location.");
|
|
888
|
-
}
|
|
889
|
-
```
|
|
890
|
-
|
|
891
|
-
You can also provide an optional prefix.
|
|
892
|
-
|
|
893
|
-
```tsx
|
|
894
|
-
import { findPage, createPage } from "@aminnairi/react-router";
|
|
895
|
-
|
|
896
|
-
const home = createPage({
|
|
897
|
-
path: "/",
|
|
898
|
-
element: () => <h1>Home</h1>
|
|
899
|
-
});
|
|
900
|
-
|
|
901
|
-
const about = createPage({
|
|
902
|
-
path: "/about",
|
|
903
|
-
element: () => <h1>About</h1>
|
|
904
|
-
});
|
|
905
|
-
|
|
906
|
-
const login = createPage({
|
|
907
|
-
path: "/login",
|
|
908
|
-
element: () => <h1>Login</h1>
|
|
909
|
-
});
|
|
910
|
-
|
|
911
|
-
const pages = [
|
|
912
|
-
home.page,
|
|
913
|
-
about.page,
|
|
914
|
-
login.page
|
|
915
|
-
];
|
|
916
|
-
|
|
917
|
-
const foundPage = findPage(pages, "/github/login", "/github");
|
|
918
|
-
|
|
919
|
-
if (foundPage) {
|
|
920
|
-
console.log("Found a page matching the current location");
|
|
921
|
-
console.log(foundPage.path);
|
|
922
|
-
} else {
|
|
923
|
-
console.log("No page matching the current location.");
|
|
924
|
-
}
|
|
877
|
+
getParameters("/users/:user", "/github/users/123/articles", "/github"); // {}
|
|
925
878
|
```
|
|
926
879
|
|
|
927
880
|
### sanitizePath
|
|
@@ -960,9 +913,7 @@ This means that you can use this library with other popular solutions for handli
|
|
|
960
913
|
|
|
961
914
|
### Transition
|
|
962
915
|
|
|
963
|
-
Support for the View Transition API is built-in and allows for painless and smooth view transition out-of-the-box
|
|
964
|
-
|
|
965
|
-
This can also easily be disabled if needed.
|
|
916
|
+
Support for the View Transition API is built-in and allows for painless and smooth view transition out-of-the-box. You can create your own transition animation, and the library also exports a `slideFadeTransition` ready to be used.
|
|
966
917
|
|
|
967
918
|
### Error handling
|
|
968
919
|
|
|
@@ -976,11 +927,42 @@ See [`LICENSE`](./LICENSE).
|
|
|
976
927
|
|
|
977
928
|
### Versions
|
|
978
929
|
|
|
930
|
+
- [`2.0.0`](#200)
|
|
931
|
+
- [`1.1.0`](#110)
|
|
979
932
|
- [`1.0.1`](#101)
|
|
980
933
|
- [`1.0.0`](#100)
|
|
981
934
|
- [`0.1.1`](#011)
|
|
982
935
|
- [`0.1.0`](#010)
|
|
983
936
|
|
|
937
|
+
### 2.0.0
|
|
938
|
+
|
|
939
|
+
#### Major changes
|
|
940
|
+
|
|
941
|
+
- The `transition` property in `createRouter` is now a function instead of a boolean, which allows for more control over the animation. This is a breaking change.
|
|
942
|
+
- A `slideFadeTransition` is now exported and can be used directly.
|
|
943
|
+
|
|
944
|
+
#### Minor changes
|
|
945
|
+
|
|
946
|
+
None.
|
|
947
|
+
|
|
948
|
+
#### Bug & security fixes
|
|
949
|
+
|
|
950
|
+
None.
|
|
951
|
+
|
|
952
|
+
### 1.1.0
|
|
953
|
+
|
|
954
|
+
#### Major changes
|
|
955
|
+
|
|
956
|
+
None.
|
|
957
|
+
|
|
958
|
+
#### Minor changes
|
|
959
|
+
|
|
960
|
+
- Added a new `useLink` hook to create components that allow for navigating to another page
|
|
961
|
+
|
|
962
|
+
#### Bug & security fixes
|
|
963
|
+
|
|
964
|
+
None.
|
|
965
|
+
|
|
984
966
|
### 1.0.1
|
|
985
967
|
|
|
986
968
|
#### Major changes
|
|
@@ -1039,4 +1021,4 @@ None.
|
|
|
1039
1021
|
|
|
1040
1022
|
#### Bug & security fixes
|
|
1041
1023
|
|
|
1042
|
-
None.
|
|
1024
|
+
None.
|
package/index.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useState, FunctionComponent, useMemo, Component, PropsWithChildren, createContext, SetStateAction, Dispatch, ReactNode, useContext, useCallback } from "react";
|
|
1
|
+
import { useEffect, useState, FunctionComponent, useMemo, Component, PropsWithChildren, createContext, SetStateAction, Dispatch, ReactNode, useContext, useCallback, memo, MouseEvent } from "react";
|
|
2
2
|
|
|
3
3
|
export type AbsolutePath<Path extends string> =
|
|
4
4
|
Path extends `${infer Start}:${string}/${infer Rest}`
|
|
@@ -25,8 +25,10 @@ export interface Page<Path extends string> {
|
|
|
25
25
|
element: FunctionComponent<PageComponentProps<Path>>
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
export type Transition = (direction: NavigationDirection, next: () => void) => void;
|
|
29
|
+
|
|
28
30
|
export interface CreateRouterOptions<Path extends string> {
|
|
29
|
-
transition?:
|
|
31
|
+
transition?: Transition,
|
|
30
32
|
prefix?: string,
|
|
31
33
|
pages: Array<Page<Path>>
|
|
32
34
|
fallback: FunctionComponent
|
|
@@ -99,7 +101,7 @@ export interface IssueProps {
|
|
|
99
101
|
|
|
100
102
|
export interface ErrorBoundaryProps {
|
|
101
103
|
fallback: FunctionComponent<IssueProps>,
|
|
102
|
-
transition
|
|
104
|
+
transition?: Transition
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
export interface ErrorBoundaryState {
|
|
@@ -177,6 +179,11 @@ const Context = createContext<ContextInterface>({
|
|
|
177
179
|
setHash: () => { }
|
|
178
180
|
});
|
|
179
181
|
|
|
182
|
+
enum NavigationDirection {
|
|
183
|
+
Forward = "pushstate",
|
|
184
|
+
Backward = "popstate"
|
|
185
|
+
}
|
|
186
|
+
|
|
180
187
|
export const useNavigateToPage = <Path extends string>(page: Page<Path>) => {
|
|
181
188
|
const { prefix } = useContext(Context);
|
|
182
189
|
|
|
@@ -187,18 +194,22 @@ export const useNavigateToPage = <Path extends string>(page: Page<Path>) => {
|
|
|
187
194
|
return path.replace(`:${parameterName}`, parameterValue);
|
|
188
195
|
}, initialPath);
|
|
189
196
|
|
|
190
|
-
console.log({ initialPath, pathWithParameters });
|
|
191
|
-
|
|
192
197
|
if (replace) {
|
|
193
198
|
window.history.replaceState(null, pathWithParameters, pathWithParameters);
|
|
194
199
|
} else {
|
|
195
200
|
window.history.pushState(null, pathWithParameters, pathWithParameters);
|
|
196
201
|
}
|
|
197
202
|
|
|
198
|
-
window.dispatchEvent(new CustomEvent(
|
|
203
|
+
window.dispatchEvent(new CustomEvent(NavigationDirection.Forward));
|
|
199
204
|
}, [page]);
|
|
200
205
|
};
|
|
201
206
|
|
|
207
|
+
export const useNavigateBack = () => {
|
|
208
|
+
return useCallback(() => {
|
|
209
|
+
window.dispatchEvent(new CustomEvent(NavigationDirection.Backward));
|
|
210
|
+
}, []);
|
|
211
|
+
}
|
|
212
|
+
|
|
202
213
|
export const useIsActivePage = (page: Page<string>) => {
|
|
203
214
|
const { pathname, prefix } = useContext(Context);
|
|
204
215
|
|
|
@@ -216,12 +227,74 @@ export const useHash = () => {
|
|
|
216
227
|
return hash;
|
|
217
228
|
};
|
|
218
229
|
|
|
219
|
-
export const
|
|
230
|
+
export const useLink = <Path extends string>(page: Page<Path>) => {
|
|
231
|
+
const Link = memo(({ children, parameters }: { children: ReactNode, parameters: Parameters<Path> }) => {
|
|
232
|
+
const { prefix } = useContext(Context);
|
|
233
|
+
const navigateToPage = useNavigateToPage(page);
|
|
234
|
+
|
|
235
|
+
const pathWithParameters = useMemo(() => {
|
|
236
|
+
return Object.entries(parameters).reduce((previousPath, [parameterName, parameterValue]) => {
|
|
237
|
+
return previousPath.replace(`:${parameterName}`, parameterValue);
|
|
238
|
+
}, sanitizePath(`${prefix ?? ""}/${page.path}`));
|
|
239
|
+
}, []);
|
|
240
|
+
|
|
241
|
+
const navigate = useCallback((event: MouseEvent) => {
|
|
242
|
+
event.preventDefault();
|
|
243
|
+
navigateToPage(parameters);
|
|
244
|
+
}, []);
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<a
|
|
248
|
+
href={pathWithParameters}
|
|
249
|
+
onClick={navigate}>
|
|
250
|
+
{children}
|
|
251
|
+
</a>
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
return Link;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export const slideFadeTransition: Transition = async (direction: NavigationDirection, next) => {
|
|
260
|
+
const transition = document.startViewTransition(() => {
|
|
261
|
+
next();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
await transition.ready;
|
|
265
|
+
|
|
266
|
+
document.documentElement.animate(
|
|
267
|
+
[
|
|
268
|
+
{ transform: 'translateX(0)', opacity: 1 },
|
|
269
|
+
{ transform: `translateX(${direction === NavigationDirection.Forward ? '100%' : '-100%'})`, opacity: 0 }
|
|
270
|
+
],
|
|
271
|
+
{
|
|
272
|
+
duration: 250,
|
|
273
|
+
easing: "ease-in-out",
|
|
274
|
+
fill: "both",
|
|
275
|
+
pseudoElement: "::view-transition-old(root)",
|
|
276
|
+
}
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
document.documentElement.animate(
|
|
280
|
+
[
|
|
281
|
+
{ transform: `translateX(${direction === NavigationDirection.Forward ? '-100%' : '100%'})`, opacity: 0 },
|
|
282
|
+
{ transform: 'translateX(0)', opacity: 1 }
|
|
283
|
+
],
|
|
284
|
+
{
|
|
285
|
+
duration: 250,
|
|
286
|
+
easing: "ease-in-out",
|
|
287
|
+
fill: "both",
|
|
288
|
+
pseudoElement: "::view-transition-new(root)",
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export const createRouter = <Path extends string>({ pages, fallback, transition, issue, prefix }: CreateRouterOptions<Path>) => {
|
|
220
294
|
const Provider = ({ children }: ProviderProps) => {
|
|
221
295
|
const [pathname, setPathname] = useState(sanitizePath(window.location.pathname));
|
|
222
296
|
const [search, setSearch] = useState(new URLSearchParams(sanitizePath(window.location.search)));
|
|
223
297
|
const [hash, setHash] = useState(window.location.hash);
|
|
224
|
-
const shouldTransitionBetweenPages = useMemo(() => typeof document.startViewTransition === "function" && withViewTransition ? true : false, [withViewTransition]);
|
|
225
298
|
|
|
226
299
|
const value = useMemo(() => {
|
|
227
300
|
return {
|
|
@@ -236,9 +309,9 @@ export const createRouter = <Path extends string>({ pages, fallback, transition:
|
|
|
236
309
|
}, [prefix, pathname, search, hash]);
|
|
237
310
|
|
|
238
311
|
useEffect(() => {
|
|
239
|
-
const
|
|
240
|
-
if (
|
|
241
|
-
|
|
312
|
+
const onNavigation = async (direction: NavigationDirection) => {
|
|
313
|
+
if (transition) {
|
|
314
|
+
transition(direction, () => {
|
|
242
315
|
setPathname(sanitizePath(window.location.pathname));
|
|
243
316
|
});
|
|
244
317
|
|
|
@@ -246,18 +319,28 @@ export const createRouter = <Path extends string>({ pages, fallback, transition:
|
|
|
246
319
|
}
|
|
247
320
|
|
|
248
321
|
setPathname(sanitizePath(window.location.pathname));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const onNavigationForward = () => {
|
|
325
|
+
onNavigation(NavigationDirection.Forward);
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const onNavigationBackward = () => {
|
|
329
|
+
onNavigation(NavigationDirection.Backward);
|
|
249
330
|
};
|
|
250
331
|
|
|
251
|
-
window.addEventListener(
|
|
332
|
+
window.addEventListener(NavigationDirection.Forward, onNavigationForward);
|
|
333
|
+
window.addEventListener(NavigationDirection.Backward, onNavigationBackward);
|
|
252
334
|
|
|
253
335
|
return () => {
|
|
254
|
-
window.removeEventListener(
|
|
336
|
+
window.removeEventListener(NavigationDirection.Forward, onNavigationForward);
|
|
337
|
+
window.removeEventListener(NavigationDirection.Backward, onNavigationBackward);
|
|
255
338
|
}
|
|
256
339
|
}, []);
|
|
257
340
|
|
|
258
341
|
return (
|
|
259
342
|
<Context.Provider value={value}>
|
|
260
|
-
<ErrorBoundary fallback={issue}
|
|
343
|
+
<ErrorBoundary fallback={issue}>
|
|
261
344
|
{children}
|
|
262
345
|
</ErrorBoundary>
|
|
263
346
|
</Context.Provider>
|
|
@@ -279,7 +362,9 @@ export const createRouter = <Path extends string>({ pages, fallback, transition:
|
|
|
279
362
|
|
|
280
363
|
if (page) {
|
|
281
364
|
return (
|
|
282
|
-
<
|
|
365
|
+
<div >
|
|
366
|
+
<page.element parameters={parameters} />
|
|
367
|
+
</div>
|
|
283
368
|
);
|
|
284
369
|
}
|
|
285
370
|
|
|
@@ -292,4 +377,4 @@ export const createRouter = <Path extends string>({ pages, fallback, transition:
|
|
|
292
377
|
View,
|
|
293
378
|
Provider,
|
|
294
379
|
};
|
|
295
|
-
}
|
|
380
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@aminnairi/react-router",
|
|
4
4
|
"description": "Type-safe router for the React library",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "2.0.0",
|
|
6
6
|
"homepage": "https://github.com/aminnairi/react-router#readme",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"bugs": {
|
|
@@ -27,4 +27,4 @@
|
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"react": ">=18.0.0"
|
|
29
29
|
}
|
|
30
|
-
}
|
|
30
|
+
}
|