routexiz 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -112,6 +112,9 @@ const navigate = useNavigate()
112
112
  navigate("/users/:id", {
113
113
  params: { id: 1 },
114
114
  query: { tab: "profile" }
115
+
116
+ // transition: 'fade',
117
+ // duration: 100
115
118
  })
116
119
  ```
117
120
 
@@ -231,8 +234,8 @@ export default function UsersList() {
231
234
  </Link>
232
235
 
233
236
  {/* disablePrefetch */}
234
- <Link to="/users/2" params={{ id: 2 }} query={{ tab: "profile" }} disablePrefetch>
235
- User 2 (prefetch on viewport)
237
+ <Link to="/users/2" params={{ id: 3 }} query={{ tab: "profile" }} disablePrefetch>
238
+ User 3
236
239
  </Link>
237
240
  </div>
238
241
  )
@@ -516,125 +519,213 @@ function Users() {
516
519
 
517
520
  ---
518
521
 
522
+ # RouterProvider
523
+ ## transition
524
+
525
+ ```ts
526
+ import "routexiz/styles.css"
527
+ export default function App() {
528
+ return <RouterProvider transition="fade" duration={100} />;
529
+ }
530
+ ```
531
+
532
+ Support `fade` | `slide` | `scale` | `none`
533
+
534
+ ---
535
+
536
+ ## transition with Wrapper
537
+
538
+ ```ts
539
+ export default function App() {
540
+ return <RouterProvider transition="fade" duration={100} wrapper={Layout} />;
541
+ }
542
+ ```
543
+
544
+ ---
545
+
519
546
  # Full Example
520
547
 
548
+ ## Users.tsx
549
+
550
+ ```tsx
551
+ // Users.tsx
552
+ import { Link } from "routexiz";
553
+
554
+ export function Users() {
555
+ return (
556
+ <div>
557
+ <h3>Users List</h3>
558
+ <Link to="/users/1" query={{ tab: [1, 2] }}>User 1</Link>
559
+ <br />
560
+ <Link to="/users/2">User 2</Link>
561
+ <br />
562
+ <Link to="/users/999">User 999 (error)</Link>
563
+ <br />
564
+ <Link to="/users/1/profile">User 1 Profile</Link>
565
+ </div>
566
+ );
567
+ }
568
+ ```
569
+
570
+ ## App.tsx
571
+
521
572
  ```ts
522
- import React from "react"
523
- import { Link, NavLink, RouterProvider, route, useLoaderData, useParams } from "routexiz"
573
+ import React, { ReactNode } from "react";
574
+ import {
575
+ Link,
576
+ RouterProvider,
577
+ setGlobalFallback,
578
+ setGlobalError,
579
+ route,
580
+ useLoaderData,
581
+ useParams,
582
+ useQuery,
583
+ useNavigate,
584
+ navigate,
585
+ } from "routexiz";
586
+
587
+ import "routexiz/styles.css"
524
588
 
525
589
  /* =========================
526
- PAGES
590
+ DEMO PAGES
527
591
  ========================= */
528
- function Home() {
529
- return <div>Home Page</div>
530
- }
531
592
 
532
- function About() {
533
- return <div>About Page</div>
593
+ function Layout({ children }: { children?: ReactNode }) {
594
+ return (
595
+ <div style={{ padding: 20 }}>
596
+ <h2>App Layout</h2>
597
+ <nav>
598
+ <Link to="/dashboard">Dashboard</Link> |{" "}
599
+ <Link to="/dashboard/stats">Dashboard Stats</Link> |{" "}
600
+ <Link to="/dashboard/settings">Dashboard Settings</Link> |{" "}
601
+ <Link to="/users">Users</Link> |{" "}
602
+ <Link to="/about">About</Link> |{" "}
603
+ <Link to="/about/team">Team</Link> |{" "}
604
+ <Link to="/contact">Contact</Link> |{" "}
605
+ <Link to="/blog/my-first-post">Blog Post</Link> |{" "}
606
+ <Link to="/search/react#top">Search React</Link> |{" "}
607
+ <Link to="/unknown/path">Unknown (404)</Link>
608
+ </nav>
609
+ <hr />
610
+ {children}
611
+ </div>
612
+ );
534
613
  }
535
614
 
536
- function Dashboard() {
537
- return <div>Dashboard Page</div>
615
+ function Dashboard({ children }: { children?: ReactNode }) {
616
+ return <div>Dashboard {children}</div>;
538
617
  }
539
618
 
540
- function Users() {
541
- return <div>Users List Page</div>
542
- }
619
+ const Users = React.lazy(() => import("./Users"));
543
620
 
544
621
  function User() {
545
- const params = useParams()
546
- const data = useLoaderData<any>()
622
+ const data = useLoaderData<any>();
623
+ const params = useParams();
624
+ const query = useQuery();
547
625
 
548
626
  return (
549
627
  <div>
550
- User Page {params.id}
628
+ <h3>User {params.id}</h3>
629
+ <div>Query: {JSON.stringify(query)}</div>
551
630
  <pre>{JSON.stringify(data, null, 2)}</pre>
552
631
  </div>
553
- )
632
+ );
554
633
  }
555
634
 
556
- /* =========================
557
- ROUTES
558
- ========================= */
559
- route("/", Home, root => {
560
- root.route("/about", About)
635
+ function About({ children }: { children?: ReactNode }) {
636
+ return (
637
+ <div>
638
+ <h2>About Page</h2>
639
+ {children}
640
+ </div>
641
+ );
642
+ }
561
643
 
562
- root.route("/dashboard", Dashboard, dash => {
644
+ function Team() {
645
+ return <div>Team Page</div>;
646
+ }
563
647
 
564
- // child
565
- dash.route("/users", Users)
648
+ function Contact() {
649
+ return <div>Contact Page</div>;
650
+ }
566
651
 
567
- dash.route("/users/:id", User, {
652
+ function Blog({ children }: { children?: ReactNode }) {
653
+ const data = useLoaderData<any>();
654
+ return (
655
+ <div>
656
+ <h3>Blog: {data.slug}</h3>
657
+ {children}
658
+ </div>
659
+ );
660
+ }
568
661
 
569
- loader: async ({ params }) => {
570
- await new Promise(r => setTimeout(r, 400))
571
- if (params.id === "999") throw new Error("User not found") // demo error
572
- return { id: params.id, name: "User " + params.id }
573
- },
574
- fallback: <div>Loading user...</div>,
575
- errorBoundary: ({ error }) => <div>Error: {String(error)}</div>
662
+ function Search() {
663
+ const query = useQuery();
664
+ const params = useParams();
576
665
 
577
- })
578
- })
666
+ const term = Array.isArray(query.term) ? query.term[0] : query.term;
579
667
 
580
- root.route("*", () => <div>Page Not Found</div>)
581
- })
668
+ return (
669
+ <div>
670
+ <h3>Search Page</h3>
671
+ <div>Term param: {params.term || "none"}</div>
672
+ <div>Term query: {term || "none"}</div>
673
+ <div>Full query: {JSON.stringify(query)}</div>
674
+ </div>
675
+ );
676
+ }
582
677
 
583
- // route("*", () => <div>Page Not Found</div>) => same
678
+ function NotFound() {
679
+ return <div>Page Not Found (404)</div>;
680
+ }
584
681
 
585
682
  /* =========================
586
- NAVIGATION
683
+ ROUTES SETUP
587
684
  ========================= */
588
- function Navbar() {
589
- return (
590
- <nav style={{ marginBottom: 20 }}>
591
- <Link to="/" className="link" activeClassName="active-link" style={{ marginRight: 10 }}>
592
- Home
593
- </Link>
594
685
 
595
- <NavLink
596
- to="/about"
597
- className={({ isActive }) => (isActive ? "active-link" : "link")}
598
- style={({ isActive }) => ({ marginRight: 10, color: isActive ? "green" : "blue" })}
599
- >
600
- About
601
- </NavLink>
602
-
603
- <NavLink
604
- to="/dashboard"
605
- className={({ isActive }) => (isActive ? "active-link" : "link")}
606
- style={({ isActive }) => ({ marginRight: 10, color: isActive ? "green" : "blue" })}
607
- >
608
- Dashboard
609
- </NavLink>
610
-
611
- <NavLink
612
- to="/dashboard/users"
613
- className={({ isActive }) => (isActive ? "active-link" : "link")}
614
- style={({ isActive }) => ({ marginRight: 10, color: isActive ? "green" : "blue" })}
615
- >
616
- Users
617
- </NavLink>
618
-
619
- <NavLink
620
- to="/dashboard/users/1"
621
- className={({ isActive }) => (isActive ? "active-link" : "link")}
622
- style={({ isActive }) => ({ marginRight: 10, color: isActive ? "green" : "blue" })}
623
- >
624
- User 1
625
- </NavLink>
626
-
627
- <NavLink
628
- to="/dashboard/users/2"
629
- className={({ isActive }) => (isActive ? "active-link" : "link")}
630
- style={({ isActive }) => ({ color: isActive ? "green" : "blue" })}
631
- >
632
- User 2
633
- </NavLink>
634
- </nav>
635
- )
686
+ function setupRoutes() {
687
+ route("/", Layout, (root) => {
688
+ root.route("/dashboard", Dashboard, (dash) => {
689
+ dash.route("/stats", () => <div>Dashboard Stats Page</div>);
690
+ dash.route("/settings", () => <div>Dashboard Settings Page</div>);
691
+ });
692
+
693
+ root.route("users", Users); // list page
694
+ root.route("users/:id/profile", () => <div>User Profile Page</div>);
695
+ root.route("users/:id/posts", () => <div>User Posts Page</div>);
696
+
697
+ root.route("/users/:id", User, {
698
+ loader: async ({ params }) => {
699
+ await new Promise((r) => setTimeout(r, 400));
700
+ if (params.id === "999") throw new Error("User not found");
701
+ return { id: params.id, name: "User " + params.id };
702
+ },
703
+ fallback: <div>Loading user...</div>,
704
+ errorBoundary: ({ error }) => <div>Error: {String(error)}</div>,
705
+ });
706
+
707
+ root.route("about", About, about => {
708
+ about.route("team", Team);
709
+ });
710
+
711
+ // root.route("about/team", Team);
712
+ root.route("contact", Contact);
713
+ root.route("*", NotFound);
714
+ });
715
+
716
+ route("/blog/:slug", Blog, {
717
+ loader: async ({ params }) => {
718
+ await new Promise((r) => setTimeout(r, 200));
719
+ return { slug: params.slug };
720
+ },
721
+ });
722
+
723
+ route("/search", Search);
724
+ route("/search/:term", Search);
636
725
  }
637
726
 
727
+ setupRoutes();
728
+
638
729
  /* =========================
639
730
  APP
640
731
  ========================= */
@@ -649,7 +740,7 @@ export default function App() {
649
740
  }
650
741
  ```
651
742
 
652
- ---
743
+ ---
653
744
 
654
745
  # Data Preloading (SSR / Advanced)
655
746
 
@@ -1,5 +1,5 @@
1
- import React from "react";
2
- import type { LinkCoreOptions } from "./LinkCore";
1
+ import React from 'react';
2
+ import type { LinkCoreOptions } from './LinkCore';
3
3
  export type LinkProps = LinkCoreOptions & React.AnchorHTMLAttributes<HTMLAnchorElement> & {
4
4
  activeClassName?: string;
5
5
  onNavigate?: (to: string) => void;
@@ -1,22 +1,24 @@
1
+ import type { TransitionName } from '../router';
1
2
  export type LinkCoreOptions = {
2
3
  to: string;
3
- transition?: string;
4
+ transition?: TransitionName;
4
5
  replace?: boolean;
5
6
  params?: Record<string, any>;
6
7
  query?: Record<string, any>;
7
8
  hash?: string;
8
9
  partialMatch?: boolean;
9
10
  disablePrefetch?: boolean;
11
+ replaceQuery?: boolean;
10
12
  };
11
- export declare function useLinkCore({ to, transition, replace, params, query, hash, partialMatch, disablePrefetch, }: LinkCoreOptions): {
13
+ export declare function useLinkCore({ to, transition, replace, params, query, hash, partialMatch, disablePrefetch, replaceQuery, }: LinkCoreOptions): {
12
14
  ref: import("react").RefObject<HTMLAnchorElement>;
13
15
  fullPath: string;
14
16
  isActive: boolean;
15
- go: () => void;
17
+ go: () => Promise<boolean>;
16
18
  };
17
19
  export declare const LinkCore: import("react").ForwardRefExoticComponent<import("react").AnchorHTMLAttributes<HTMLAnchorElement> & {
18
20
  fullPath: string;
19
- onNavigate: () => void;
21
+ onNavigate: () => Promise<boolean> | boolean;
20
22
  onPrefetch?: () => void;
21
23
  disablePrefetch?: boolean;
22
24
  } & import("react").RefAttributes<HTMLAnchorElement>>;
@@ -1,5 +1,5 @@
1
- import React from "react";
2
- import type { LinkCoreOptions } from "./LinkCore";
1
+ import React from 'react';
2
+ import type { LinkCoreOptions } from './LinkCore';
3
3
  export type NavLinkProps = LinkCoreOptions & Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'className' | 'style'> & {
4
4
  className?: string | ((opts: {
5
5
  isActive: boolean;
@@ -1,4 +1,4 @@
1
- export declare function RenderTree({ chain, data }: {
1
+ export declare function RenderTree({ chain, data, }: {
2
2
  chain: any[];
3
3
  data: Record<string, any>;
4
4
  }): JSX.Element;
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { TransitionStage } from '../router';
3
+ export declare const DEFAULT_DURATION = 200;
4
+ type Props = {
5
+ name?: string;
6
+ stage: TransitionStage;
7
+ duration?: number;
8
+ children: ReactNode;
9
+ };
10
+ export declare function Transition({ name, stage, duration, children, }: Props): JSX.Element;
11
+ export {};
@@ -1,4 +1,5 @@
1
- export * from "./Link";
2
- export * from "./NavLink";
3
- export { useLinkCore } from "./LinkCore";
4
- export type { LinkCoreOptions } from "./LinkCore";
1
+ export * from './Link';
2
+ export * from './NavLink';
3
+ export * from './Transition';
4
+ export { useLinkCore } from './LinkCore';
5
+ export type { LinkCoreOptions } from './LinkCore';
package/build/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export * from "./router";
2
- export * from "./components";
1
+ export * from './router';
2
+ export * from './components';
@@ -1 +1 @@
1
- import n,{createContext as t,useContext as e,Suspense as r,useState as a,useEffect as o,forwardRef as s,useRef as i}from"react";import{jsx as c,jsxs as h,Fragment as u}from"react/jsx-runtime";import l from"joinclass";let p=[];function d(){p=[]}function f(){return p}function m(n){const t={guard:e=>(n.guardFns||(n.guardFns=[]),n.guardFns.push(e),t),middleware:e=>(n.middlewares||(n.middlewares=[]),n.middlewares.push(e),t),meta:e=>(n.meta=e,t),route(t,e,r){const a={path:t,component:e,children:[],options:{},guardFns:[],middlewares:[],parent:n};"function"==typeof r?m(a)(r):r&&(a.options=r),n.children.push(a);const o={guard:n=>(a.guardFns?.push(n),o),middleware:n=>(a.middlewares?.push(n),o),meta:n=>(a.meta=n,o),route(n,t,e){const r={path:n,component:t,children:[],options:{},guardFns:[],middlewares:[],parent:a};return"function"==typeof e?m(r)(e):e&&(r.options=e),a.children.push(r),o}};return o}};return n=>{n(t)}}function y(n,t,e){const r={path:n,component:t,children:[],options:{}};"function"==typeof e?m(r)(e):e&&(r.options=e),p.push(r)}const g=t(null),w=t(null),v=t(null),q=t({loading:!1,path:"",pendingPath:""}),P=t({name:"",stage:"idle"});function b(){const n=e(g);try{return n?.read()}catch{return}}function E(){const n=e(w);if(!n)throw new Error("RouteContext not found");return n}function k(){return e(v)??{}}function N(){return E().query??{}}function S(){return e(q)}function x(){return e(P)}function R(){const{meta:n,params:t,path:e,query:r}=E();let a;try{a=b()}catch{}return{data:a,params:t??{},query:r??{},path:e,meta:n}}let A=!1,C=!1,F=c("div",{children:"Loading..."}),I=({error:n})=>h("div",{children:["Global Error: ",String(n)]});function K(n,t){A=n,t&&(F=t)}function M(n,t){C=n,t&&(I=({error:n})=>t(n))}function B(){return A}function D(){return C}function U(){return F}function L(){return I}function O(n){return n.startsWith("/")||(n="/"+n),"/"!==n&&n.endsWith("/")&&(n=n.slice(0,-1)),n}function j(n){return O(n).split("/").filter(Boolean)}function W(n){const[t,e]=n.split("#"),[r,a]=t.split("?"),o={};return a&&a.split("&").forEach(n=>{const[t,e]=n.split("=");t&&(o[decodeURIComponent(t)]=decodeURIComponent(e??""))}),{path:O(r),query:o,hash:e??""}}function $(n,t){const{path:e,query:r,hash:a}=W(t),o=j(e),s=n.map(n=>({node:n,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:n,segIndex:t,params:e,chain:i}=s.pop(),c=j(n.path);let h=0;const u={...e};for(;h<c.length;h++){const n=c[h],e=o[t+h];if(!e)break;if(n.startsWith(":"))u[n.slice(1)]=decodeURIComponent(e);else if(n!==e)break}if(h!==c.length)continue;const l=t+h,p=[...i,{node:n,params:u,query:r,hash:a}];if(l===o.length)return p;for(const t of n.children)s.push({node:t,segIndex:l,params:u,chain:p})}return null}function J(n){const t={};if(!n)return t;if("string"==typeof n){const e=new URLSearchParams(n);e.forEach((n,r)=>{const a=e.getAll(r);t[r]=a.length>1?a:a[0]})}else for(const e in n){const r=n[e];Array.isArray(r)?t[e]=r.map(n=>String(n)):null!=r&&(t[e]=String(r))}return t}function G(n,t,e,r){const{path:a,query:o,hash:s}=W(n);let i=O(a);if(t)for(const n in t)i=i.replace(`:${n}`,encodeURIComponent(t[n]));const c={...o,...e??{}};if(Object.keys(c).length){const n=new URLSearchParams;for(const t in c){const e=c[t];Array.isArray(e)?e.forEach(e=>n.append(t,String(e))):n.append(t,String(e))}const t=n.toString();t&&(i+=`?${t}`)}return r?i+=`#${r}`:s&&(i+=`#${s}`),i}class T extends n.Component{constructor(n){super(n),this.reset=()=>{this.setState(n=>({hasError:!1,error:null,resetKey:n.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(n){return{hasError:!0,error:n}}render(){if(this.state.hasError){const n=this.props.errorBoundary??(D()?L():void 0),t=this.props.fallback??h("div",{children:["Error: ",String(this.state.error)]});return n?c(n,{error:this.state.error}):t}return c(n.Fragment,{children:this.props.children},this.state.resetKey)}}function z({resource:n,errorBoundary:t,children:e}){try{return n?.read(),c(u,{children:e})}catch(n){if(n instanceof Promise)throw n;const e=t??(D()?L():void 0);if(e)return c(e,{error:n});throw n}}function H({chain:n,data:t}){return c(u,{children:function e(a=0){const o=n[a];if(!o)return null;const s=o.node.component,i=t[o.node.path];if(!s)return e(a+1);let h=o.node.options?.fallback,u=o.node.parent;for(;!h&&u;)h=u.options?.fallback,u=u.parent;!h&&B()&&(h=U());const l=Object.assign({},...n.slice(0,a+1).map(n=>n.node.meta));return c(T,{fallback:h,errorBoundary:o.node.options?.errorBoundary,children:c(r,{fallback:h,children:c(w.Provider,{value:{path:o.node.path,params:o.params,query:J(o.node.query??window.location.search),hash:o.node.hash??"",meta:l},children:c(g.Provider,{value:i,children:c(v.Provider,{value:o.params,children:c(z,{resource:i,errorBoundary:o.node.options?.errorBoundary,children:c(s,{children:e(a+1)})})})})})})})}()})}async function Q(n){const t=$(f(),n);if(!t)return{};const e={};t.forEach(n=>{const t=n.node.options?.loader;t&&(e[n.node.path]=Y(JSON.stringify({path:n.node.path,params:n.params,query:n.query,hash:n.hash}),()=>t({params:n.params,path:n.node.path,query:n.query,hash:n.hash}),n.node.options?.ttl))});for(const n in e)e[n].read();return e}const V=new Map;function X(n=1/0){const t=Date.now();let e=0;for(const[r,a]of V.entries())if(a.expiry<=t&&(V.delete(r),e++,e>=n))break}function Y(n,t,e,r){const a=Date.now(),o=V.get(n);if(o&&!r&&(!e||o.expiry>a))return o.resource;const s=Z(t());return V.set(n,{resource:s,expiry:a+(e??0)}),s}function Z(n,t){let e=t?"success":"pending",r=t??null;const a=n.then(n=>{e="success",r=n},n=>{e="error",r=n});return{read(){if("pending"===e)throw a;if("error"===e)throw r;return r},update(n){r=n,e="success"},get:()=>r}}let _=null;function nn(){return _}function tn(n,t){const e=f(),[r,a]=n.split("?"),o={...J(a),...t?.query??{}},s=$(e,G(r,t?.params,o,t?.hash));s&&en(s,{forceReload:!1})}function en(n,t){const e={};return n.forEach(n=>{const r=n.node.options?.loader;if(!r)return;const a=JSON.stringify({path:n.node.path,params:n.params,query:n.query,hash:n.hash}),o=n.node.options?.ttl,s=Y(a,()=>r({params:n.params,path:n.node.path,query:n.query,hash:n.hash}),o,t?.forceReload);e[n.node.path]=s}),e}function rn(){const[n,t]=a({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"",stage:"idle"}});async function e(n,e){const r=f();let a=$(r,n)??$(r,"*")??[];for(const t of a)for(const e of t.node.guardFns||[]){if(!await e({params:t.params,path:t.node.path,query:t.query,hash:t.hash}))return console.warn("Navigation blocked by guard",n)}for(const n of a)for(const t of n.node.middlewares||[])await t({params:n.params,path:n.node.path,query:n.query,hash:n.hash}).catch(console.error);t(t=>({...t,loading:!0,pendingPath:n,transition:{name:e?.transition||"fade",stage:"exiting"}}));const o=en(a,e);t(t=>({...t,chain:a,data:{...t.data,...o},loading:!1,path:n,pendingPath:"",transition:{name:e?.transition||"fade",stage:"entering"}})),window.scrollTo(0,0)}return o(()=>{_=e,e(window.location.pathname),window.addEventListener("popstate",()=>e(window.location.pathname))},[]),c(q.Provider,{value:{loading:n.loading,path:n.path,pendingPath:n.pendingPath},children:c(P.Provider,{value:n.transition,children:c(H,{chain:n.chain,data:n.data})})})}let an=[],on=[];function sn(n){an.push(n)}function cn(n){on.push(n)}async function hn(n,t,e=!1){const r={...t,forceReload:t?.forceReload??!0},a=G(n,r.params,r.query,r.hash),o=await async function(n){return Promise.all(an.map(t=>t(n))).then(n=>n.every(n=>!1!==n))}(a);if(!o)return console.warn("Navigation blocked by beforeNavigation hook",a);e?window.history.replaceState({},"",a):window.history.pushState({},"",a),nn()?.(a,r),async function(n){for(const t of on)await t(n)}(a)}function un(n,t){return hn(n,t,!1)}function ln(n,t){return un(n,t)}function pn(n){return S().path===n}function dn(n,t){return()=>tn(n,t)}function fn(){const n=(n,t)=>hn(G(n,t?.params,t?.query,t?.hash),t);return n.replace=(n,t)=>hn(G(n,t?.params,t?.query,t?.hash),t,!0),n}function mn({to:n,transition:t,replace:e,params:r,query:a,hash:s,partialMatch:c,disablePrefetch:h}){const u=fn(),l=S(),p=i(null),d=G(n,r,a,s),f=c?l.path?.startsWith(d):l.path===d;return o(()=>{if(h)return;const n=p.current;if(!n)return;const t=new IntersectionObserver(n=>{n.some(n=>n.isIntersecting)&&(tn(d),t.disconnect())},{rootMargin:"200px"});return t.observe(n),()=>t.disconnect()},[d,h]),{ref:p,fullPath:d,isActive:f,go:()=>{e?u.replace(n,{transition:t,params:r,query:a,hash:s}):u(n,{transition:t,params:r,query:a,hash:s})}}}un.replace=(n,t)=>hn(n,t,!0);const yn=s(({fullPath:n,onNavigate:t,onPrefetch:e,onClick:r,onMouseEnter:a,onKeyDown:o,disablePrefetch:s,...i},h)=>c("a",{ref:h,href:n,...i,onClick:n=>{r&&r(n),n.metaKey||n.ctrlKey||n.shiftKey||(n.preventDefault(),t())},onMouseEnter:n=>{a&&a(n),s||e?.()},onKeyDown:n=>{"Enter"===n.key&&(n.preventDefault(),t()),o&&o(n)}}));function gn(n){const{to:t,transition:e,replace:r,activeClassName:a="active",onNavigate:o,partialMatch:s,params:i,query:h,hash:u,className:p,disablePrefetch:d,...f}=n,{ref:m,fullPath:y,isActive:g,go:w}=mn({to:t,transition:e,replace:r,params:i,query:h,hash:u,partialMatch:s,disablePrefetch:d});return c(yn,{ref:m,fullPath:y,onNavigate:()=>{w(),o?.(y)},onPrefetch:()=>tn(y),className:l(p,g&&a),disablePrefetch:d,...f})}function wn(n){const{to:t,transition:e,replace:r,params:a,query:o,hash:s,partialMatch:i,className:h,style:u,disablePrefetch:p,...d}=n,{ref:f,fullPath:m,isActive:y,go:g}=mn({to:t,transition:e,replace:r,params:a,query:o,hash:s,partialMatch:i,disablePrefetch:p}),w="function"==typeof h?h({isActive:y}):h,v="function"==typeof u?u({isActive:y}):u;return c(yn,{ref:f,fullPath:m,onNavigate:g,onPrefetch:()=>tn(m),className:l(w),style:v,disablePrefetch:p,...d})}yn.displayName="LinkCore";export{gn as Link,g as LoaderDataContext,wn as NavLink,v as ParamsContext,w as RouteContext,rn as RouterProvider,q as RouterStateContext,P as TransitionContext,cn as addAfterNavigationHook,sn as addBeforeNavigationHook,G as buildPath,X as cleanupCache,d as clearRoutes,Z as createResource,L as getGlobalError,U as getGlobalFallback,nn as getGlobalResolve,Y as getResource,f as getRoutes,D as getUseGlobalError,B as getUseGlobalFallback,Q as loadRouteData,$ as matchRouteChain,un as navigate,J as normalizeQuery,W as parseQueryHash,tn as prefetch,ln as redirect,y as route,M as setGlobalError,K as setGlobalFallback,mn as useLinkCore,b as useLoaderData,fn as useNavigate,S as useNavigation,k as useParams,dn as usePrefetch,N as useQuery,E as useRouteContext,pn as useRouteMatch,R as useRouterData,x as useTransition};
1
+ import n,{createContext as t,useContext as e,useState as r,useRef as o,useEffect as a,Suspense as i,forwardRef as s}from"react";import{jsx as c,Fragment as u,jsxs as l}from"react/jsx-runtime";import h from"joinclass";const d=new Map;function p(n=1/0){const t=Date.now();let e=0;for(const[r,o]of d.entries())if(o.expiry<=t&&(d.delete(r),e++,e>=n))break}function f(n,t,e,r){const o=Date.now(),a=d.get(n);if(a&&!r&&(!e||a.expiry>o))return a.resource;const i=m(t());return d.set(n,{resource:i,expiry:o+(e??0)}),i}function m(n,t){let e=t?"success":"pending",r=t??null;const o=n.then(n=>{e="success",r=n},n=>{e="error",r=n});return{read(){if("pending"===e)throw o;if("error"===e)throw r;return r},update(n){r=n,e="success"},get:()=>r}}function y(n){return n.startsWith("/")||(n="/"+n),"/"!==n&&n.endsWith("/")&&(n=n.slice(0,-1)),n}function g(n){return y(n).split("/").filter(Boolean)}function w(n){const[t,e]=n.split("#"),[r,o]=t.split("?"),a={};return o&&o.split("&").forEach(n=>{const[t,e]=n.split("=");if(!t)return;const r=decodeURIComponent(t),o=decodeURIComponent(e??"");void 0!==a[r]?Array.isArray(a[r])?a[r].push(o):a[r]=[a[r],o]:a[r]=o}),{path:y(r),query:a,hash:e??""}}function v(n,t){const e=n[t];return Array.isArray(e)?e[0]:e}function P(n){const t={};if(!n)return t;if("string"==typeof n){const e=new URLSearchParams(n);e.forEach((n,r)=>{const o=e.getAll(r);t[r]=o.length>1?o:o[0]})}else for(const e in n){const r=n[e];Array.isArray(r)?t[e]=r.map(n=>String(n)):null!=r&&(t[e]=String(r))}return t}function b(n){const t=g(n);let e=10*t.length;for(const n of t)"*"===n?e+=0:n.startsWith(":")?e+=1:e+=5;return e}function q(n){return[...n].sort((n,t)=>b(t.path)-b(n.path))}function E(n,t){const{path:e,query:r,hash:o}=w(t),a=g(e),i=q(n).map(n=>({node:n,segIndex:0,params:{},chain:[]}));for(;i.length;){const{node:n,segIndex:t,params:e,chain:s}=i.pop(),c=g(n.path),u={...e};let l=0;for(;l<c.length;l++){const n=c[l],e=a[t+l];if(!e)break;if("*"===n){u["*"]=a.slice(t+l).join("/"),l=c.length;break}if(n.startsWith(":"))u[n.slice(1)]=decodeURIComponent(e);else if(n!==e)break}if(l!==c.length)continue;const h=t+l,d=[...s,{node:n,params:u,query:r,hash:o}];if(h===a.length)return d;const p=q(n.children||[]);for(let n=p.length-1;n>=0;n--){const t=g(p[n].path).some(n=>n.startsWith(":"));i.push({node:p[n],segIndex:t?0:h,params:u,chain:d})}}return null}function k(n,t,e,r,o){const{path:a,query:i,hash:s}=w(n);let c=y(a);if(t)for(const n in t)c=c.replace(`:${n}`,encodeURIComponent(t[n]));const u=o?.replaceQuery?{...e??{}}:{...i,...e??{}},l=new URLSearchParams;for(const n in u){const t=u[n];Array.isArray(t)?t.forEach(t=>l.append(n,String(t))):l.append(n,String(t))}const h=l.toString();return h&&(c+=`?${h}`),r?c+=`#${r}`:s&&(c+=`#${s}`),c}function x(n){return JSON.stringify(Object.keys(n||{}).sort().reduce((t,e)=>(t[e]=n[e],t),{}))}function N(n,t){return`${n.path}|${x(t.params)}|${x(t.query)}|${t.hash}`}let S=[];function A(){S=[]}function R(){return S}function K(n){const t={guard:e=>(n.guardFns||(n.guardFns=[]),n.guardFns.push(e),t),middleware:e=>(n.middlewares||(n.middlewares=[]),n.middlewares.push(e),t),meta:e=>(n.meta={...n.meta||{},...e},t),route(t,e,r){const o={path:y(t),component:e,children:[],options:{},guardFns:[],middlewares:[],parent:n};return"function"==typeof r?K(o)(r):r&&(o.options=r),n.children.push(o),K(o).api}};return Object.assign(n=>n(t),{api:t})}function C(n,t,e){const r={path:y(n),component:t,children:[],options:{},parent:void 0};return"function"==typeof e?K(r)(e):e&&(r.options=e),S.push(r),K(r).api}const $=t(null),I=t(null),M=t(null),j=t({loading:!1,path:"",pendingPath:""}),F=t({name:"none",stage:"idle"});function L(){const n=e($);try{return n?.read()}catch{return}}function O(){const n=e(I);if(!n)throw new Error("RouteContext not found");return n}function B(){return e(M)??{}}function D(){try{const n=O();if(n?.query)return n.query}catch{}const{query:n}=w(window.location.pathname+window.location.search+window.location.hash);return n}function Q(){return e(j)}function U(){return e(F)}function W(){try{return L()}catch{return}}function T(){const{meta:n,params:t,path:e,query:r}=O();return{data:W(),params:t??{},query:r??{},path:e,meta:n}}const G=200;function J({name:n="fade",stage:t,duration:e=200,children:i}){const[s,d]=r(i),[p,f]=r(null),m=o();return a(()=>{m.current&&clearTimeout(m.current),"exiting"===t&&f(s),"entering"===t&&(d(i),m.current=window.setTimeout(()=>{f(null)},e))},[i,t,e]),"idle"===t?c(u,{children:i}):l("div",{className:h("rtx",`rtx-${n}`),children:[p&&c("div",{className:"rtx-exit",children:p}),c("div",{className:h("rtx-enter",`rtx-${t}`),children:s})]})}let z=!1,H=!1,V=c("div",{children:"Loading..."});const X=({error:n})=>l("div",{children:["Global Error: ",String(n)]});let Y=null;function Z(n,t){z=n,t&&(V=t)}function _(n,t){H=n,Y=t??null}function nn(){return z}function tn(){return H}function en(){return V}function rn(){return Y?({error:n})=>Y(n):X}class on extends n.Component{constructor(n){super(n),this.reset=()=>{this.setState(n=>({hasError:!1,error:null,resetKey:n.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(n){return{hasError:!0,error:n}}render(){if(this.state.hasError){const n=this.props.errorBoundary??(tn()?rn():void 0);return n?c(n,{error:this.state.error,params:this.props.params}):this.props.fallback??l("div",{children:["Error: ",String(this.state.error)]})}return c(n.Fragment,{children:this.props.children},this.state.resetKey)}}function an({resource:t,errorBoundary:e,children:r,resetKey:o}){const[a,i]=n.useState(null);n.useEffect(()=>{i(null)},[o]);try{return t?.read(),a&&i(null),c(u,{children:r})}catch(n){if(n instanceof Promise)throw n;const t=e??(tn()?rn():void 0);if(t)return c(t,{error:n});throw n}}function sn({chain:n,data:t}){return c(u,{children:function e(r=0){const o=n[r];if(!o)return null;const a=o.node.component;if(!a)return e(r+1);const s={params:o.params,path:o.node.path,query:o.query,hash:o.hash},u=function(n,t){let e=n;for(;e;){const n=e.options?.fallback;if(n)return"function"==typeof n?n(t):n;e=e.parent}return nn()?en():null}(o.node,s),l=Object.assign({},...n.slice(0,r+1).map(n=>n.node.meta)),h=t[N(o.node,s)];return c(on,{fallback:u,errorBoundary:o.node.options?.errorBoundary,params:o.params,children:c(i,{fallback:u,children:c(I.Provider,{value:{path:o.node.path,params:o.params,query:P(o.query),hash:o.hash??"",meta:l},children:c($.Provider,{value:h,children:c(M.Provider,{value:o.params,children:c(an,{resource:h,errorBoundary:o.node.options?.errorBoundary,resetKey:o.params.id||o.params,children:c(a,{children:e(r+1)})})})})})})})}()})}let cn=null;function un(){return cn}function ln(n,t){const e=R(),[r,o]=n.split("?"),a={...P(o),...t?.query??{}},i=E(e,k(r,t?.params,a,t?.hash));i&&hn(i,{forceReload:!1})}function hn(n,t){const e={};return n.forEach(n=>{const r=n.node.options?.loader;if(!r)return;const o={params:n.params,path:n.node.path,query:n.query,hash:n.hash},a=N(n.node,o),i=n.node.options?.ttl,s=t?.forceReload??n.node.options?.shouldReload?.({prev:void 0,next:o})??!0;e[a]=f(a,()=>Promise.resolve(r(o)),i,s)}),e}function dn({transition:n,duration:t,wrapper:e}){const[i,s]=r({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:t??G}),u=o(0);async function l(e,r){const o=++u.current,a=R(),i=E(a,e)??E(a,"*")??[];for(const n of i)for(const t of n.node.guardFns||[]){if(!await t({params:n.params,path:n.node.path,query:n.query,hash:n.hash}))return console.warn("Navigation blocked",e),!1}for(const n of i)for(const t of n.node.middlewares||[])await t({params:n.params,path:n.node.path,query:n.query,hash:n.hash}).catch(console.error);const c=function(n,t,e){if(t?.transition)return t.transition;for(let t=n.length-1;t>=0;t--){const e=n[t].node;if(e.options?.transition)return e.options.transition;if(e.meta?.transition)return e.meta.transition}return e??"none"}(i,r,n),l=function(n,t,e){if(null!=t?.duration)return t.duration;for(let t=n.length-1;t>=0;t--){const e=n[t].node;if(null!=e.options?.duration)return e.options.duration}return null!=e?e:G}(i,r,t);s(n=>({...n,loading:!0,pendingPath:e,transition:{name:c,stage:"exiting"},duration:l})),await new Promise(n=>setTimeout(n,l));const h=hn(i,r);return o===u.current&&(s(n=>({...n,chain:i,data:{...n.data,...h},loading:!1,path:e,pendingPath:"",transition:{name:c,stage:"entering"},duration:l})),window.scrollTo(0,0),!0)}a(()=>{cn=l;const n=()=>window.location.pathname+window.location.search+window.location.hash;l(n());const t=()=>l(n());window.addEventListener("popstate",t);const e=()=>{const t=E(R(),n());if(!t)return;const e={};t.forEach(n=>{n.node.options?.revalidateOnFocus&&Object.assign(e,hn([n],{forceReload:!0}))}),Object.keys(e).length&&s(n=>({...n,data:{...n.data,...e}}))};return window.addEventListener("focus",e),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",e)}},[]);const h=c(sn,{chain:i.chain,data:i.data},i.path);let d=h;if("none"!==i.transition.name&&(d=c(J,{name:i.transition.name,stage:i.transition.stage,duration:i.duration,children:h})),e){d=c(e,{children:d})}return c(j.Provider,{value:{loading:i.loading,path:i.path,pendingPath:i.pendingPath},children:c(F.Provider,{value:i.transition,children:d})})}const pn=[],fn=[];function mn(n){pn.push(n)}function yn(n){fn.push(n)}async function gn(n,t,e=!1){const r={...t,forceReload:t?.forceReload??!0},o=k(n,r.params,r.query,r.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(n){return(await Promise.all(pn.map(t=>t(n)))).every(n=>!1!==n)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=un();if(!r.shallow&&a){if(!await a(o,r))return console.warn("Navigation blocked by route guard",o),!1}return e?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(n){for(const t of fn)await t(n)}(o),!0}function wn(n,t){return gn(n,t,!1)}function vn(n,t){return wn(n,t)}function Pn(){const n=(n,t)=>gn(n,t);return n.replace=(n,t)=>gn(n,t,!0),n}function bn(n){return Q().path===n}function qn(n,t){return()=>ln(n,t)}function En({to:n,transition:t,replace:e,params:r,query:i,hash:s,partialMatch:c,disablePrefetch:u,replaceQuery:l}){const h=Pn(),d=Q(),p=o(null),f=k(n,r,i,s,{replaceQuery:l}),m=c?d.path?.startsWith(f):d.path===f;return a(()=>{if(u)return;const n=p.current;if(!n)return;const t=new IntersectionObserver(n=>{n.some(n=>n.isIntersecting)&&(ln(f),t.disconnect())},{rootMargin:"200px"});return t.observe(n),()=>t.disconnect()},[f,u]),{ref:p,fullPath:f,isActive:m,go:async()=>e?h.replace(n,{transition:t,params:r,query:i,hash:s}):h(n,{transition:t,params:r,query:i,hash:s})}}wn.replace=(n,t)=>gn(n,t,!0);const kn=s(({fullPath:n,onNavigate:t,onPrefetch:e,onClick:r,onMouseEnter:o,onKeyDown:a,disablePrefetch:i,...s},u)=>c("a",{ref:u,href:n,...s,onClick:async e=>{if(r&&r(e),e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;e.preventDefault();await t()||console.warn("Navigation blocked:",n)},onMouseEnter:n=>{o&&o(n),i||e?.()},onKeyDown:async e=>{if("Enter"===e.key){e.preventDefault();await t()||console.warn("Navigation blocked:",n)}a&&a(e)}}));function xn(n){const{to:t,transition:e,replace:r,activeClassName:o="active",onNavigate:a,partialMatch:i,params:s,query:u,hash:l,className:d,disablePrefetch:p,replaceQuery:f,...m}=n,{ref:y,fullPath:g,isActive:w,go:v}=En({to:t,transition:e,replace:r,params:s,query:u,hash:l,partialMatch:i,disablePrefetch:p,replaceQuery:f});return c(kn,{ref:y,fullPath:g,onNavigate:async()=>{const n=await v();return a?.(g),n},onPrefetch:()=>ln(g),className:h(d,w&&o),disablePrefetch:p,...m})}function Nn(n){const{to:t,transition:e,replace:r,params:o,query:a,hash:i,partialMatch:s,className:u,style:l,disablePrefetch:d,replaceQuery:p,...f}=n,{ref:m,fullPath:y,isActive:g,go:w}=En({to:t,transition:e,replace:r,params:o,query:a,hash:i,partialMatch:s,disablePrefetch:d,replaceQuery:p}),v="function"==typeof u?u({isActive:g}):u,P="function"==typeof l?l({isActive:g}):l;return c(kn,{ref:m,fullPath:y,onNavigate:async()=>(await w(),!0),onPrefetch:()=>ln(y),className:h(v),style:P,disablePrefetch:d,...f})}kn.displayName="LinkCore";export{G as DEFAULT_DURATION,xn as Link,$ as LoaderDataContext,Nn as NavLink,M as ParamsContext,I as RouteContext,dn as RouterProvider,j as RouterStateContext,J as Transition,F as TransitionContext,yn as addAfterNavigationHook,mn as addBeforeNavigationHook,N as buildCacheKey,k as buildPath,p as cleanupCache,A as clearRoutes,m as createResource,rn as getGlobalError,en as getGlobalFallback,un as getGlobalResolve,v as getQueryFirst,f as getResource,R as getRoutes,tn as getUseGlobalError,nn as getUseGlobalFallback,E as matchRouteChain,wn as navigate,y as normalize,P as normalizeQuery,w as parseQueryHash,ln as prefetch,vn as redirect,C as route,_ as setGlobalError,Z as setGlobalFallback,g as split,En as useLinkCore,L as useLoaderData,Pn as useNavigate,Q as useNavigation,B as useParams,qn as usePrefetch,D as useQuery,O as useRouteContext,bn as useRouteMatch,T as useRouterData,W as useSafeLoaderData,U as useTransition};
package/build/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("joinclass");let n=[];function o(){return n}function a(e){const t={guard:r=>(e.guardFns||(e.guardFns=[]),e.guardFns.push(r),t),middleware:r=>(e.middlewares||(e.middlewares=[]),e.middlewares.push(r),t),meta:r=>(e.meta=r,t),route(t,r,n){const o={path:t,component:r,children:[],options:{},guardFns:[],middlewares:[],parent:e};"function"==typeof n?a(o)(n):n&&(o.options=n),e.children.push(o);const s={guard:e=>(o.guardFns?.push(e),s),middleware:e=>(o.middlewares?.push(e),s),meta:e=>(o.meta=e,s),route(e,t,r){const n={path:e,component:t,children:[],options:{},guardFns:[],middlewares:[],parent:o};return"function"==typeof r?a(n)(r):r&&(n.options=r),o.children.push(n),s}};return s}};return e=>{e(t)}}const s=e.createContext(null),i=e.createContext(null),c=e.createContext(null),u=e.createContext({loading:!1,path:"",pendingPath:""}),h=e.createContext({name:"",stage:"idle"});function p(){const t=e.useContext(s);try{return t?.read()}catch{return}}function l(){const t=e.useContext(i);if(!t)throw new Error("RouteContext not found");return t}function d(){return e.useContext(u)}let f=!1,x=!1,m=t.jsx("div",{children:"Loading..."}),y=({error:e})=>t.jsxs("div",{children:["Global Error: ",String(e)]});function g(){return f}function v(){return x}function w(){return m}function b(){return y}function P(e){return e.startsWith("/")||(e="/"+e),"/"!==e&&e.endsWith("/")&&(e=e.slice(0,-1)),e}function q(e){return P(e).split("/").filter(Boolean)}function C(e){const[t,r]=e.split("#"),[n,o]=t.split("?"),a={};return o&&o.split("&").forEach(e=>{const[t,r]=e.split("=");t&&(a[decodeURIComponent(t)]=decodeURIComponent(r??""))}),{path:P(n),query:a,hash:r??""}}function R(e,t){const{path:r,query:n,hash:o}=C(t),a=q(r),s=e.map(e=>({node:e,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:e,segIndex:t,params:r,chain:i}=s.pop(),c=q(e.path);let u=0;const h={...r};for(;u<c.length;u++){const e=c[u],r=a[t+u];if(!r)break;if(e.startsWith(":"))h[e.slice(1)]=decodeURIComponent(r);else if(e!==r)break}if(u!==c.length)continue;const p=t+u,l=[...i,{node:e,params:h,query:n,hash:o}];if(p===a.length)return l;for(const t of e.children)s.push({node:t,segIndex:p,params:h,chain:l})}return null}function j(e){const t={};if(!e)return t;if("string"==typeof e){const r=new URLSearchParams(e);r.forEach((e,n)=>{const o=r.getAll(n);t[n]=o.length>1?o:o[0]})}else for(const r in e){const n=e[r];Array.isArray(n)?t[r]=n.map(e=>String(e)):null!=n&&(t[r]=String(n))}return t}function k(e,t,r,n){const{path:o,query:a,hash:s}=C(e);let i=P(o);if(t)for(const e in t)i=i.replace(`:${e}`,encodeURIComponent(t[e]));const c={...a,...r??{}};if(Object.keys(c).length){const e=new URLSearchParams;for(const t in c){const r=c[t];Array.isArray(r)?r.forEach(r=>e.append(t,String(r))):e.append(t,String(r))}const t=e.toString();t&&(i+=`?${t}`)}return n?i+=`#${n}`:s&&(i+=`#${s}`),i}class E extends e.Component{constructor(e){super(e),this.reset=()=>{this.setState(e=>({hasError:!1,error:null,resetKey:e.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}render(){if(this.state.hasError){const e=this.props.errorBoundary??(v()?b():void 0),r=this.props.fallback??t.jsxs("div",{children:["Error: ",String(this.state.error)]});return e?t.jsx(e,{error:this.state.error}):r}return t.jsx(e.Fragment,{children:this.props.children},this.state.resetKey)}}function N({resource:e,errorBoundary:r,children:n}){try{return e?.read(),t.jsx(t.Fragment,{children:n})}catch(e){if(e instanceof Promise)throw e;const n=r??(v()?b():void 0);if(n)return t.jsx(n,{error:e});throw e}}function S({chain:r,data:n}){return t.jsx(t.Fragment,{children:function o(a=0){const u=r[a];if(!u)return null;const h=u.node.component,p=n[u.node.path];if(!h)return o(a+1);let l=u.node.options?.fallback,d=u.node.parent;for(;!l&&d;)l=d.options?.fallback,d=d.parent;!l&&g()&&(l=w());const f=Object.assign({},...r.slice(0,a+1).map(e=>e.node.meta));return t.jsx(E,{fallback:l,errorBoundary:u.node.options?.errorBoundary,children:t.jsx(e.Suspense,{fallback:l,children:t.jsx(i.Provider,{value:{path:u.node.path,params:u.params,query:j(u.node.query??window.location.search),hash:u.node.hash??"",meta:f},children:t.jsx(s.Provider,{value:p,children:t.jsx(c.Provider,{value:u.params,children:t.jsx(N,{resource:p,errorBoundary:u.node.options?.errorBoundary,children:t.jsx(h,{children:o(a+1)})})})})})})})}()})}const F=new Map;function A(e,t,r,n){const o=Date.now(),a=F.get(e);if(a&&!n&&(!r||a.expiry>o))return a.resource;const s=D(t());return F.set(e,{resource:s,expiry:o+(r??0)}),s}function D(e,t){let r=t?"success":"pending",n=t??null;const o=e.then(e=>{r="success",n=e},e=>{r="error",n=e});return{read(){if("pending"===r)throw o;if("error"===r)throw n;return n},update(e){n=e,r="success"},get:()=>n}}let L=null;function M(){return L}function I(e,t){const r=o(),[n,a]=e.split("?"),s={...j(a),...t?.query??{}},i=R(r,k(n,t?.params,s,t?.hash));i&&K(i,{forceReload:!1})}function K(e,t){const r={};return e.forEach(e=>{const n=e.node.options?.loader;if(!n)return;const o=JSON.stringify({path:e.node.path,params:e.params,query:e.query,hash:e.hash}),a=e.node.options?.ttl,s=A(o,()=>n({params:e.params,path:e.node.path,query:e.query,hash:e.hash}),a,t?.forceReload);r[e.node.path]=s}),r}let B=[],G=[];async function U(e,t,r=!1){const n={...t,forceReload:t?.forceReload??!0},o=k(e,n.params,n.query,n.hash),a=await async function(e){return Promise.all(B.map(t=>t(e))).then(e=>e.every(e=>!1!==e))}(o);if(!a)return console.warn("Navigation blocked by beforeNavigation hook",o);r?window.history.replaceState({},"",o):window.history.pushState({},"",o),M()?.(o,n),async function(e){for(const t of G)await t(e)}(o)}function O(e,t){return U(e,t,!1)}function W(){const e=(e,t)=>U(k(e,t?.params,t?.query,t?.hash),t);return e.replace=(e,t)=>U(k(e,t?.params,t?.query,t?.hash),t,!0),e}function $({to:t,transition:r,replace:n,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:c}){const u=W(),h=d(),p=e.useRef(null),l=k(t,o,a,s),f=i?h.path?.startsWith(l):h.path===l;return e.useEffect(()=>{if(c)return;const e=p.current;if(!e)return;const t=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(I(l),t.disconnect())},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[l,c]),{ref:p,fullPath:l,isActive:f,go:()=>{n?u.replace(t,{transition:r,params:o,query:a,hash:s}):u(t,{transition:r,params:o,query:a,hash:s})}}}O.replace=(e,t)=>U(e,t,!0);const H=e.forwardRef(({fullPath:e,onNavigate:r,onPrefetch:n,onClick:o,onMouseEnter:a,onKeyDown:s,disablePrefetch:i,...c},u)=>t.jsx("a",{ref:u,href:e,...c,onClick:e=>{o&&o(e),e.metaKey||e.ctrlKey||e.shiftKey||(e.preventDefault(),r())},onMouseEnter:e=>{a&&a(e),i||n?.()},onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),r()),s&&s(e)}}));H.displayName="LinkCore",exports.Link=function(e){const{to:n,transition:o,replace:a,activeClassName:s="active",onNavigate:i,partialMatch:c,params:u,query:h,hash:p,className:l,disablePrefetch:d,...f}=e,{ref:x,fullPath:m,isActive:y,go:g}=$({to:n,transition:o,replace:a,params:u,query:h,hash:p,partialMatch:c,disablePrefetch:d});return t.jsx(H,{ref:x,fullPath:m,onNavigate:()=>{g(),i?.(m)},onPrefetch:()=>I(m),className:r(l,y&&s),disablePrefetch:d,...f})},exports.LoaderDataContext=s,exports.NavLink=function(e){const{to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,className:h,style:p,disablePrefetch:l,...d}=e,{ref:f,fullPath:x,isActive:m,go:y}=$({to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,disablePrefetch:l}),g="function"==typeof h?h({isActive:m}):h,v="function"==typeof p?p({isActive:m}):p;return t.jsx(H,{ref:f,fullPath:x,onNavigate:y,onPrefetch:()=>I(x),className:r(g),style:v,disablePrefetch:l,...d})},exports.ParamsContext=c,exports.RouteContext=i,exports.RouterProvider=function(){const[r,n]=e.useState({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"",stage:"idle"}});async function a(e,t){const r=o();let a=R(r,e)??R(r,"*")??[];for(const t of a)for(const r of t.node.guardFns||[]){if(!await r({params:t.params,path:t.node.path,query:t.query,hash:t.hash}))return console.warn("Navigation blocked by guard",e)}for(const e of a)for(const t of e.node.middlewares||[])await t({params:e.params,path:e.node.path,query:e.query,hash:e.hash}).catch(console.error);n(r=>({...r,loading:!0,pendingPath:e,transition:{name:t?.transition||"fade",stage:"exiting"}}));const s=K(a,t);n(r=>({...r,chain:a,data:{...r.data,...s},loading:!1,path:e,pendingPath:"",transition:{name:t?.transition||"fade",stage:"entering"}})),window.scrollTo(0,0)}return e.useEffect(()=>{L=a,a(window.location.pathname),window.addEventListener("popstate",()=>a(window.location.pathname))},[]),t.jsx(u.Provider,{value:{loading:r.loading,path:r.path,pendingPath:r.pendingPath},children:t.jsx(h.Provider,{value:r.transition,children:t.jsx(S,{chain:r.chain,data:r.data})})})},exports.RouterStateContext=u,exports.TransitionContext=h,exports.addAfterNavigationHook=function(e){G.push(e)},exports.addBeforeNavigationHook=function(e){B.push(e)},exports.buildPath=k,exports.cleanupCache=function(e=1/0){const t=Date.now();let r=0;for(const[n,o]of F.entries())if(o.expiry<=t&&(F.delete(n),r++,r>=e))break},exports.clearRoutes=function(){n=[]},exports.createResource=D,exports.getGlobalError=b,exports.getGlobalFallback=w,exports.getGlobalResolve=M,exports.getResource=A,exports.getRoutes=o,exports.getUseGlobalError=v,exports.getUseGlobalFallback=g,exports.loadRouteData=async function(e){const t=R(o(),e);if(!t)return{};const r={};t.forEach(e=>{const t=e.node.options?.loader;t&&(r[e.node.path]=A(JSON.stringify({path:e.node.path,params:e.params,query:e.query,hash:e.hash}),()=>t({params:e.params,path:e.node.path,query:e.query,hash:e.hash}),e.node.options?.ttl))});for(const e in r)r[e].read();return r},exports.matchRouteChain=R,exports.navigate=O,exports.normalizeQuery=j,exports.parseQueryHash=C,exports.prefetch=I,exports.redirect=function(e,t){return O(e,t)},exports.route=function(e,t,r){const o={path:e,component:t,children:[],options:{}};"function"==typeof r?a(o)(r):r&&(o.options=r),n.push(o)},exports.setGlobalError=function(e,t){x=e,t&&(y=({error:e})=>t(e))},exports.setGlobalFallback=function(e,t){f=e,t&&(m=t)},exports.useLinkCore=$,exports.useLoaderData=p,exports.useNavigate=W,exports.useNavigation=d,exports.useParams=function(){return e.useContext(c)??{}},exports.usePrefetch=function(e,t){return()=>I(e,t)},exports.useQuery=function(){return l().query??{}},exports.useRouteContext=l,exports.useRouteMatch=function(e){return d().path===e},exports.useRouterData=function(){const{meta:e,params:t,path:r,query:n}=l();let o;try{o=p()}catch{}return{data:o,params:t??{},query:n??{},path:r,meta:e}},exports.useTransition=function(){return e.useContext(h)};
1
+ "use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("joinclass");const n=new Map;function o(e,t,r,o){const s=Date.now(),i=n.get(e);if(i&&!o&&(!r||i.expiry>s))return i.resource;const c=a(t());return n.set(e,{resource:c,expiry:s+(r??0)}),c}function a(e,t){let r=t?"success":"pending",n=t??null;const o=e.then(e=>{r="success",n=e},e=>{r="error",n=e});return{read(){if("pending"===r)throw o;if("error"===r)throw n;return n},update(e){n=e,r="success"},get:()=>n}}function s(e){return e.startsWith("/")||(e="/"+e),"/"!==e&&e.endsWith("/")&&(e=e.slice(0,-1)),e}function i(e){return s(e).split("/").filter(Boolean)}function c(e){const[t,r]=e.split("#"),[n,o]=t.split("?"),a={};return o&&o.split("&").forEach(e=>{const[t,r]=e.split("=");if(!t)return;const n=decodeURIComponent(t),o=decodeURIComponent(r??"");void 0!==a[n]?Array.isArray(a[n])?a[n].push(o):a[n]=[a[n],o]:a[n]=o}),{path:s(n),query:a,hash:r??""}}function u(e){const t={};if(!e)return t;if("string"==typeof e){const r=new URLSearchParams(e);r.forEach((e,n)=>{const o=r.getAll(n);t[n]=o.length>1?o:o[0]})}else for(const r in e){const n=e[r];Array.isArray(n)?t[r]=n.map(e=>String(e)):null!=n&&(t[r]=String(n))}return t}function l(e){const t=i(e);let r=10*t.length;for(const e of t)"*"===e?r+=0:e.startsWith(":")?r+=1:r+=5;return r}function p(e){return[...e].sort((e,t)=>l(t.path)-l(e.path))}function h(e,t){const{path:r,query:n,hash:o}=c(t),a=i(r),s=p(e).map(e=>({node:e,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:e,segIndex:t,params:r,chain:c}=s.pop(),u=i(e.path),l={...r};let h=0;for(;h<u.length;h++){const e=u[h],r=a[t+h];if(!r)break;if("*"===e){l["*"]=a.slice(t+h).join("/"),h=u.length;break}if(e.startsWith(":"))l[e.slice(1)]=decodeURIComponent(r);else if(e!==r)break}if(h!==u.length)continue;const d=t+h,f=[...c,{node:e,params:l,query:n,hash:o}];if(d===a.length)return f;const x=p(e.children||[]);for(let e=x.length-1;e>=0;e--){const t=i(x[e].path).some(e=>e.startsWith(":"));s.push({node:x[e],segIndex:t?0:d,params:l,chain:f})}}return null}function d(e,t,r,n,o){const{path:a,query:i,hash:u}=c(e);let l=s(a);if(t)for(const e in t)l=l.replace(`:${e}`,encodeURIComponent(t[e]));const p=o?.replaceQuery?{...r??{}}:{...i,...r??{}},h=new URLSearchParams;for(const e in p){const t=p[e];Array.isArray(t)?t.forEach(t=>h.append(e,String(t))):h.append(e,String(t))}const d=h.toString();return d&&(l+=`?${d}`),n?l+=`#${n}`:u&&(l+=`#${u}`),l}function f(e){return JSON.stringify(Object.keys(e||{}).sort().reduce((t,r)=>(t[r]=e[r],t),{}))}function x(e,t){return`${e.path}|${f(t.params)}|${f(t.query)}|${t.hash}`}let m=[];function y(){return m}function g(e){const t={guard:r=>(e.guardFns||(e.guardFns=[]),e.guardFns.push(r),t),middleware:r=>(e.middlewares||(e.middlewares=[]),e.middlewares.push(r),t),meta:r=>(e.meta={...e.meta||{},...r},t),route(t,r,n){const o={path:s(t),component:r,children:[],options:{},guardFns:[],middlewares:[],parent:e};return"function"==typeof n?g(o)(n):n&&(o.options=n),e.children.push(o),g(o).api}};return Object.assign(e=>e(t),{api:t})}const w=e.createContext(null),v=e.createContext(null),b=e.createContext(null),P=e.createContext({loading:!1,path:"",pendingPath:""}),j=e.createContext({name:"none",stage:"idle"});function q(){const t=e.useContext(w);try{return t?.read()}catch{return}}function C(){const t=e.useContext(v);if(!t)throw new Error("RouteContext not found");return t}function R(){return e.useContext(P)}function k(){try{return q()}catch{return}}const E=200;function N({name:n="fade",stage:o,duration:a=200,children:s}){const[i,c]=e.useState(s),[u,l]=e.useState(null),p=e.useRef();return e.useEffect(()=>{p.current&&clearTimeout(p.current),"exiting"===o&&l(i),"entering"===o&&(c(s),p.current=window.setTimeout(()=>{l(null)},a))},[s,o,a]),"idle"===o?t.jsx(t.Fragment,{children:s}):t.jsxs("div",{className:r("rtx",`rtx-${n}`),children:[u&&t.jsx("div",{className:"rtx-exit",children:u}),t.jsx("div",{className:r("rtx-enter",`rtx-${o}`),children:i})]})}let S=!1,A=!1,F=t.jsx("div",{children:"Loading..."});const L=({error:e})=>t.jsxs("div",{children:["Global Error: ",String(e)]});let D=null;function K(){return S}function Q(){return A}function I(){return F}function M(){return D?({error:e})=>D(e):L}class U extends e.Component{constructor(e){super(e),this.reset=()=>{this.setState(e=>({hasError:!1,error:null,resetKey:e.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}render(){if(this.state.hasError){const e=this.props.errorBoundary??(Q()?M():void 0);return e?t.jsx(e,{error:this.state.error,params:this.props.params}):this.props.fallback??t.jsxs("div",{children:["Error: ",String(this.state.error)]})}return t.jsx(e.Fragment,{children:this.props.children},this.state.resetKey)}}function $({resource:r,errorBoundary:n,children:o,resetKey:a}){const[s,i]=e.useState(null);e.useEffect(()=>{i(null)},[a]);try{return r?.read(),s&&i(null),t.jsx(t.Fragment,{children:o})}catch(e){if(e instanceof Promise)throw e;const r=n??(Q()?M():void 0);if(r)return t.jsx(r,{error:e});throw e}}function O({chain:r,data:n}){return t.jsx(t.Fragment,{children:function o(a=0){const s=r[a];if(!s)return null;const i=s.node.component;if(!i)return o(a+1);const c={params:s.params,path:s.node.path,query:s.query,hash:s.hash},l=function(e,t){let r=e;for(;r;){const e=r.options?.fallback;if(e)return"function"==typeof e?e(t):e;r=r.parent}return K()?I():null}(s.node,c),p=Object.assign({},...r.slice(0,a+1).map(e=>e.node.meta)),h=n[x(s.node,c)];return t.jsx(U,{fallback:l,errorBoundary:s.node.options?.errorBoundary,params:s.params,children:t.jsx(e.Suspense,{fallback:l,children:t.jsx(v.Provider,{value:{path:s.node.path,params:s.params,query:u(s.query),hash:s.hash??"",meta:p},children:t.jsx(w.Provider,{value:h,children:t.jsx(b.Provider,{value:s.params,children:t.jsx($,{resource:h,errorBoundary:s.node.options?.errorBoundary,resetKey:s.params.id||s.params,children:t.jsx(i,{children:o(a+1)})})})})})})})}()})}let T=null;function B(){return T}function G(e,t){const r=y(),[n,o]=e.split("?"),a={...u(o),...t?.query??{}},s=h(r,d(n,t?.params,a,t?.hash));s&&W(s,{forceReload:!1})}function W(e,t){const r={};return e.forEach(e=>{const n=e.node.options?.loader;if(!n)return;const a={params:e.params,path:e.node.path,query:e.query,hash:e.hash},s=x(e.node,a),i=e.node.options?.ttl,c=t?.forceReload??e.node.options?.shouldReload?.({prev:void 0,next:a})??!0;r[s]=o(s,()=>Promise.resolve(n(a)),i,c)}),r}const H=[],z=[];async function J(e,t,r=!1){const n={...t,forceReload:t?.forceReload??!0},o=d(e,n.params,n.query,n.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(e){return(await Promise.all(H.map(t=>t(e)))).every(e=>!1!==e)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=B();if(!n.shallow&&a){if(!await a(o,n))return console.warn("Navigation blocked by route guard",o),!1}return r?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(e){for(const t of z)await t(e)}(o),!0}function _(e,t){return J(e,t,!1)}function V(){const e=(e,t)=>J(e,t);return e.replace=(e,t)=>J(e,t,!0),e}function X({to:t,transition:r,replace:n,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:c,replaceQuery:u}){const l=V(),p=R(),h=e.useRef(null),f=d(t,o,a,s,{replaceQuery:u}),x=i?p.path?.startsWith(f):p.path===f;return e.useEffect(()=>{if(c)return;const e=h.current;if(!e)return;const t=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(G(f),t.disconnect())},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[f,c]),{ref:h,fullPath:f,isActive:x,go:async()=>n?l.replace(t,{transition:r,params:o,query:a,hash:s}):l(t,{transition:r,params:o,query:a,hash:s})}}_.replace=(e,t)=>J(e,t,!0);const Y=e.forwardRef(({fullPath:e,onNavigate:r,onPrefetch:n,onClick:o,onMouseEnter:a,onKeyDown:s,disablePrefetch:i,...c},u)=>t.jsx("a",{ref:u,href:e,...c,onClick:async t=>{if(o&&o(t),t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;t.preventDefault();await r()||console.warn("Navigation blocked:",e)},onMouseEnter:e=>{a&&a(e),i||n?.()},onKeyDown:async t=>{if("Enter"===t.key){t.preventDefault();await r()||console.warn("Navigation blocked:",e)}s&&s(t)}}));Y.displayName="LinkCore",exports.DEFAULT_DURATION=E,exports.Link=function(e){const{to:n,transition:o,replace:a,activeClassName:s="active",onNavigate:i,partialMatch:c,params:u,query:l,hash:p,className:h,disablePrefetch:d,replaceQuery:f,...x}=e,{ref:m,fullPath:y,isActive:g,go:w}=X({to:n,transition:o,replace:a,params:u,query:l,hash:p,partialMatch:c,disablePrefetch:d,replaceQuery:f});return t.jsx(Y,{ref:m,fullPath:y,onNavigate:async()=>{const e=await w();return i?.(y),e},onPrefetch:()=>G(y),className:r(h,g&&s),disablePrefetch:d,...x})},exports.LoaderDataContext=w,exports.NavLink=function(e){const{to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,className:l,style:p,disablePrefetch:h,replaceQuery:d,...f}=e,{ref:x,fullPath:m,isActive:y,go:g}=X({to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,disablePrefetch:h,replaceQuery:d}),w="function"==typeof l?l({isActive:y}):l,v="function"==typeof p?p({isActive:y}):p;return t.jsx(Y,{ref:x,fullPath:m,onNavigate:async()=>(await g(),!0),onPrefetch:()=>G(m),className:r(w),style:v,disablePrefetch:h,...f})},exports.ParamsContext=b,exports.RouteContext=v,exports.RouterProvider=function({transition:r,duration:n,wrapper:o}){const[a,s]=e.useState({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:n??E}),i=e.useRef(0);async function c(e,t){const o=++i.current,a=y(),c=h(a,e)??h(a,"*")??[];for(const t of c)for(const r of t.node.guardFns||[]){if(!await r({params:t.params,path:t.node.path,query:t.query,hash:t.hash}))return console.warn("Navigation blocked",e),!1}for(const e of c)for(const t of e.node.middlewares||[])await t({params:e.params,path:e.node.path,query:e.query,hash:e.hash}).catch(console.error);const u=function(e,t,r){if(t?.transition)return t.transition;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(r.options?.transition)return r.options.transition;if(r.meta?.transition)return r.meta.transition}return r??"none"}(c,t,r),l=function(e,t,r){if(null!=t?.duration)return t.duration;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(null!=r.options?.duration)return r.options.duration}return null!=r?r:E}(c,t,n);s(t=>({...t,loading:!0,pendingPath:e,transition:{name:u,stage:"exiting"},duration:l})),await new Promise(e=>setTimeout(e,l));const p=W(c,t);return o===i.current&&(s(t=>({...t,chain:c,data:{...t.data,...p},loading:!1,path:e,pendingPath:"",transition:{name:u,stage:"entering"},duration:l})),window.scrollTo(0,0),!0)}e.useEffect(()=>{T=c;const e=()=>window.location.pathname+window.location.search+window.location.hash;c(e());const t=()=>c(e());window.addEventListener("popstate",t);const r=()=>{const t=h(y(),e());if(!t)return;const r={};t.forEach(e=>{e.node.options?.revalidateOnFocus&&Object.assign(r,W([e],{forceReload:!0}))}),Object.keys(r).length&&s(e=>({...e,data:{...e.data,...r}}))};return window.addEventListener("focus",r),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",r)}},[]);const u=t.jsx(O,{chain:a.chain,data:a.data},a.path);let l=u;if("none"!==a.transition.name&&(l=t.jsx(N,{name:a.transition.name,stage:a.transition.stage,duration:a.duration,children:u})),o){const e=o;l=t.jsx(e,{children:l})}return t.jsx(P.Provider,{value:{loading:a.loading,path:a.path,pendingPath:a.pendingPath},children:t.jsx(j.Provider,{value:a.transition,children:l})})},exports.RouterStateContext=P,exports.Transition=N,exports.TransitionContext=j,exports.addAfterNavigationHook=function(e){z.push(e)},exports.addBeforeNavigationHook=function(e){H.push(e)},exports.buildCacheKey=x,exports.buildPath=d,exports.cleanupCache=function(e=1/0){const t=Date.now();let r=0;for(const[o,a]of n.entries())if(a.expiry<=t&&(n.delete(o),r++,r>=e))break},exports.clearRoutes=function(){m=[]},exports.createResource=a,exports.getGlobalError=M,exports.getGlobalFallback=I,exports.getGlobalResolve=B,exports.getQueryFirst=function(e,t){const r=e[t];return Array.isArray(r)?r[0]:r},exports.getResource=o,exports.getRoutes=y,exports.getUseGlobalError=Q,exports.getUseGlobalFallback=K,exports.matchRouteChain=h,exports.navigate=_,exports.normalize=s,exports.normalizeQuery=u,exports.parseQueryHash=c,exports.prefetch=G,exports.redirect=function(e,t){return _(e,t)},exports.route=function(e,t,r){const n={path:s(e),component:t,children:[],options:{},parent:void 0};return"function"==typeof r?g(n)(r):r&&(n.options=r),m.push(n),g(n).api},exports.setGlobalError=function(e,t){A=e,D=t??null},exports.setGlobalFallback=function(e,t){S=e,t&&(F=t)},exports.split=i,exports.useLinkCore=X,exports.useLoaderData=q,exports.useNavigate=V,exports.useNavigation=R,exports.useParams=function(){return e.useContext(b)??{}},exports.usePrefetch=function(e,t){return()=>G(e,t)},exports.useQuery=function(){try{const e=C();if(e?.query)return e.query}catch{}const{query:e}=c(window.location.pathname+window.location.search+window.location.hash);return e},exports.useRouteContext=C,exports.useRouteMatch=function(e){return R().path===e},exports.useRouterData=function(){const{meta:e,params:t,path:r,query:n}=C();return{data:k(),params:t??{},query:n??{},path:r,meta:e}},exports.useSafeLoaderData=k,exports.useTransition=function(){return e.useContext(j)};
@@ -1,8 +1,5 @@
1
- import React from "react";
2
- import type { ReactNode } from "react";
3
- import type { BuilderRouteAPI, RouteNode, RouteOptions } from "./types";
1
+ import type { BuilderRouteAPI, ExtractParams, RouteComponent, RouteNode, RouteOptions } from './types';
4
2
  export declare function clearRoutes(): void;
5
3
  export declare function getRoutes(): RouteNode[];
6
- export declare function route<P = any, R = any>(path: string, component?: React.ComponentType<{
7
- children?: ReactNode;
8
- }>, options?: ((api: BuilderRouteAPI) => void) | RouteOptions<P, R>): void;
4
+ export declare function route<Path extends string, R = any>(path: Path, component?: RouteComponent, options?: ((api: BuilderRouteAPI<ExtractParams<Path>>) => void) | RouteOptions<ExtractParams<Path>, R>): BuilderRouteAPI<ExtractParams<Path>>;
5
+ export declare function loadRouteData(path: string): Promise<Record<string, any>>;
@@ -1,9 +1,8 @@
1
- import React from "react";
2
- export type TransitionStage = "idle" | "entering" | "exiting";
1
+ import type { Query, TransitionName, TransitionStage } from './types';
3
2
  export type RouteContextType<M = any> = {
4
3
  path: string;
5
4
  params: Record<string, any>;
6
- query: Record<string, string | string[]>;
5
+ query: Query;
7
6
  hash?: string;
8
7
  meta?: M;
9
8
  };
@@ -13,14 +12,14 @@ export type RouterState = {
13
12
  pendingPath: string;
14
13
  };
15
14
  export type TransitionState = {
16
- name: string;
15
+ name: TransitionName;
17
16
  stage: TransitionStage;
18
17
  };
19
- export declare const LoaderDataContext: React.Context<any>;
20
- export declare const RouteContext: React.Context<RouteContextType<any> | null>;
21
- export declare const ParamsContext: React.Context<any>;
22
- export declare const RouterStateContext: React.Context<RouterState>;
23
- export declare const TransitionContext: React.Context<TransitionState>;
18
+ export declare const LoaderDataContext: import("react").Context<any>;
19
+ export declare const RouteContext: import("react").Context<RouteContextType<any> | null>;
20
+ export declare const ParamsContext: import("react").Context<any>;
21
+ export declare const RouterStateContext: import("react").Context<RouterState>;
22
+ export declare const TransitionContext: import("react").Context<TransitionState>;
24
23
  export declare function useLoaderData<T = unknown>(): T | undefined;
25
24
  export declare function useRouteContext<M = any>(): RouteContextType<M>;
26
25
  export declare function useParams<P extends Record<string, any> = Record<string, any>>(): P;
@@ -34,4 +33,5 @@ export type RouterData<T = unknown, P extends Record<string, any> = Record<strin
34
33
  path: string;
35
34
  meta?: any;
36
35
  };
37
- export declare function useRouterData<T = unknown, P extends Record<string, any> = Record<string, any>, Q extends Record<string, any> = Record<string, string>>(): RouterData<T, P, Q>;
36
+ export declare function useSafeLoaderData<T>(): T | undefined;
37
+ export declare function useRouterData<T = unknown, P extends Record<string, unknown> = Record<string, unknown>, Q extends Record<string, string> = Record<string, string>>(): RouterData<T, P, Q>;
@@ -1,4 +1,14 @@
1
- import type { NavigateOpts } from "./navigation";
2
- export declare function getGlobalResolve(): ((path: string, opts?: any) => void) | null;
1
+ import React from 'react';
2
+ import type { TransitionName } from './types';
3
+ import type { NavigateOpts } from './navigation';
4
+ type RouterProviderProps = {
5
+ transition?: TransitionName;
6
+ duration?: number;
7
+ wrapper?: React.ComponentType<{
8
+ children: React.ReactNode;
9
+ }>;
10
+ };
11
+ export declare function getGlobalResolve(): ((path: string, opts?: NavigateOpts) => Promise<boolean>) | null;
3
12
  export declare function prefetch(path: string, options?: NavigateOpts): void;
4
- export declare function RouterProvider(): JSX.Element;
13
+ export declare function RouterProvider({ transition: defaultTransition, duration: providerDuration, wrapper, }: RouterProviderProps): JSX.Element;
14
+ export {};
@@ -1,8 +1,8 @@
1
- export { clearRoutes, getRoutes, route } from "./builder";
2
- export * from "./contexts";
3
- export * from "./core";
4
- export * from "./navigation";
5
- export * from "./resource";
6
- export * from "./setting";
7
- export * from "./types";
8
- export * from "./utils";
1
+ export { clearRoutes, getRoutes, route } from './builder';
2
+ export * from './contexts';
3
+ export * from './core';
4
+ export * from './navigation';
5
+ export * from './resource';
6
+ export * from './setting';
7
+ export * from './types';
8
+ export * from './utils';
@@ -1,20 +1,23 @@
1
+ import type { TransitionName } from './types';
1
2
  export type NavigateOpts = {
2
- transition?: string;
3
+ transition?: TransitionName;
3
4
  forceReload?: boolean;
4
5
  params?: Record<string, string | number>;
5
6
  query?: Record<string, string | number>;
6
7
  hash?: string;
8
+ shallow?: boolean;
9
+ duration?: number;
7
10
  };
8
11
  export declare function addBeforeNavigationHook(fn: (path: string) => boolean | Promise<boolean>): void;
9
12
  export declare function addAfterNavigationHook(fn: (path: string) => void | Promise<void>): void;
10
- export declare function navigate(path: string, opts?: NavigateOpts): Promise<void>;
13
+ export declare function navigate(path: string, opts?: NavigateOpts): Promise<boolean>;
11
14
  export declare namespace navigate {
12
- var replace: (path: string, opts?: NavigateOpts) => Promise<void>;
15
+ var replace: (path: string, opts?: NavigateOpts) => Promise<boolean>;
13
16
  }
14
- export declare function redirect(path: string, opts?: NavigateOpts): Promise<void>;
15
- export declare function useRouteMatch(pathPattern: string): boolean;
16
- export declare function usePrefetch(path: string, opts?: NavigateOpts): () => void;
17
+ export declare function redirect(path: string, opts?: NavigateOpts): Promise<boolean>;
17
18
  export declare function useNavigate(): {
18
- (path: string, opts?: NavigateOpts): Promise<void>;
19
- replace(path: string, opts?: NavigateOpts): Promise<void>;
19
+ (path: string, opts?: NavigateOpts): Promise<boolean>;
20
+ replace(path: string, opts?: NavigateOpts): Promise<boolean>;
20
21
  };
22
+ export declare function useRouteMatch(pathPattern: string): boolean;
23
+ export declare function usePrefetch(path: string, opts?: NavigateOpts): () => void;
@@ -1,4 +1,3 @@
1
- export declare function loadRouteData(path: string): Promise<Record<string, any>>;
2
1
  export declare function cleanupCache(limit?: number): void;
3
2
  export declare function getResource<T>(key: string, loader: () => Promise<T>, ttl?: number, forceReload?: boolean): {
4
3
  read(): unknown;
@@ -1,8 +1,10 @@
1
+ import React from 'react';
2
+ export type ErrorProps = {
3
+ error: unknown;
4
+ };
1
5
  export declare function setGlobalFallback(enable: boolean, fallback?: React.ReactNode): void;
2
- export declare function setGlobalError(enable: boolean, render?: (error: any) => React.ReactElement | null): void;
6
+ export declare function setGlobalError(enable: boolean, render?: (error: unknown) => React.ReactElement | null): void;
3
7
  export declare function getUseGlobalFallback(): boolean;
4
8
  export declare function getUseGlobalError(): boolean;
5
- export declare function getGlobalFallback(): import("react").ReactNode;
6
- export declare function getGlobalError(): import("react").ComponentType<{
7
- error: any;
8
- }>;
9
+ export declare function getGlobalFallback(): React.ReactNode;
10
+ export declare function getGlobalError(): React.FC<ErrorProps>;
@@ -1,4 +1,6 @@
1
- import type { ReactNode } from "react";
1
+ import type { ReactNode } from 'react';
2
+ export type TransitionName = 'fade' | 'slide' | 'scale' | 'none';
3
+ export type TransitionStage = 'idle' | 'entering' | 'exiting';
2
4
  type LoaderContext<P = any> = {
3
5
  params: P;
4
6
  path: string;
@@ -6,21 +8,39 @@ type LoaderContext<P = any> = {
6
8
  hash?: string;
7
9
  };
8
10
  export type RouteOptions<P = any, R = any> = {
9
- loader?: (context: LoaderContext<P>) => Promise<R>;
10
- meta?: Record<string, any>;
11
+ loader?: (context: LoaderContext<P>) => Promise<R> | R;
12
+ ttl?: number;
13
+ shouldReload?: (context: {
14
+ prev?: LoaderContext<P>;
15
+ next: LoaderContext<P>;
16
+ }) => boolean;
17
+ fallback?: ReactNode | ((context: LoaderContext<P>) => ReactNode);
11
18
  errorBoundary?: React.ComponentType<{
12
19
  error: any;
20
+ params?: P;
13
21
  }>;
14
- fallback?: React.ReactNode;
15
- ttl?: number;
22
+ preload?: boolean;
23
+ keepPreviousData?: boolean;
24
+ revalidateOnFocus?: boolean;
25
+ meta?: Record<string, any>;
26
+ priority?: number;
27
+ transition?: TransitionName;
28
+ duration?: number;
16
29
  };
30
+ export type QueryValue = string | string[];
31
+ export type Query = Record<string, QueryValue>;
17
32
  export type GuardFn<P = any> = (context: LoaderContext<P>) => boolean | Promise<boolean>;
18
33
  export type MiddlewareFn<P = any> = (context: LoaderContext<P>) => any | Promise<any>;
19
- export type RouteNode<P = any, R = any> = {
20
- path: string;
21
- component?: React.ComponentType<{
34
+ export type RouteComponent = React.ComponentType<{
35
+ children?: ReactNode;
36
+ }> | (() => Promise<{
37
+ default: React.ComponentType<{
22
38
  children?: ReactNode;
23
39
  }>;
40
+ }>);
41
+ export type RouteNode<P = any, R = any> = {
42
+ path: string;
43
+ component?: RouteComponent;
24
44
  children: RouteNode[];
25
45
  options: RouteOptions<P, R>;
26
46
  guardFns?: GuardFn<P>[];
@@ -28,12 +48,15 @@ export type RouteNode<P = any, R = any> = {
28
48
  meta?: Record<string, any>;
29
49
  parent?: RouteNode;
30
50
  };
31
- export type BuilderRouteAPI = {
32
- guard: (fn: GuardFn) => BuilderRouteAPI;
33
- middleware: (fn: MiddlewareFn) => BuilderRouteAPI;
34
- route: <P = any, R = any>(path: string, component?: React.ComponentType<{
35
- children?: ReactNode;
36
- }>, options?: ((api: BuilderRouteAPI) => void) | RouteOptions<P, R>) => BuilderRouteAPI;
37
- meta: (obj: Record<string, any>) => BuilderRouteAPI;
51
+ export type ExtractParams<Path extends string> = Path extends `${string}:${infer Param}/${infer Rest}` ? {
52
+ [K in Param | keyof ExtractParams<`/${Rest}`>]: string;
53
+ } : Path extends `${string}:${infer Param}` ? {
54
+ [K in Param]: string;
55
+ } : object;
56
+ export type BuilderRouteAPI<Params = any> = {
57
+ guard: (fn: GuardFn<Params>) => BuilderRouteAPI<Params>;
58
+ middleware: (fn: MiddlewareFn<Params>) => BuilderRouteAPI<Params>;
59
+ meta: (obj: Record<string, any>) => BuilderRouteAPI<Params>;
60
+ route: <Path extends string, R = any>(path: Path, component?: RouteComponent, options?: ((api: BuilderRouteAPI<Params & ExtractParams<Path>>) => void) | RouteOptions<Params & ExtractParams<Path>, R>) => BuilderRouteAPI<Params & ExtractParams<Path>>;
38
61
  };
39
62
  export {};
@@ -1,9 +1,15 @@
1
- import type { RouteNode } from "./types";
1
+ import type { Query, RouteNode } from './types';
2
+ export declare function normalize(path: string): string;
3
+ export declare function split(path: string): string[];
2
4
  export declare function parseQueryHash(pathname: string): {
3
5
  path: string;
4
- query: Record<string, string>;
6
+ query: Query;
5
7
  hash: string;
6
8
  };
7
- export declare function matchRouteChain(routes: RouteNode[], pathname: string): any[] | null;
9
+ export declare function getQueryFirst(q: Query, key: string): string;
8
10
  export declare function normalizeQuery(input?: string | Record<string, any>): Record<string, string | string[]>;
9
- export declare function buildPath(path: string, params?: Record<string, any>, query?: Record<string, any>, hash?: string): string;
11
+ export declare function matchRouteChain(routes: RouteNode[], pathname: string): any[] | null;
12
+ export declare function buildPath(path: string, params?: Record<string, any>, query?: Record<string, any>, hash?: string, options?: {
13
+ replaceQuery?: boolean;
14
+ }): string;
15
+ export declare function buildCacheKey(node: RouteNode, context: any): string;
@@ -0,0 +1,77 @@
1
+ /* =========================
2
+ BASE
3
+ ========================= */
4
+ .rtx {
5
+ position: relative;
6
+ }
7
+
8
+ .rtx-enter,
9
+ .rtx-exit {
10
+ position: absolute;
11
+ width: 100%;
12
+ top: 0;
13
+ left: 0;
14
+ }
15
+
16
+ /* =========================
17
+ FADE
18
+ ========================= */
19
+ .rtx-fade .rtx-entering {
20
+ animation: rtxFadeIn 0.25s ease forwards;
21
+ }
22
+
23
+ .rtx-fade .rtx-exit {
24
+ animation: rtxFadeOut 0.25s ease forwards;
25
+ }
26
+
27
+ @keyframes rtxFadeIn {
28
+ from { opacity: 0; }
29
+ to { opacity: 1; }
30
+ }
31
+
32
+ @keyframes rtxFadeOut {
33
+ from { opacity: 1; }
34
+ to { opacity: 0; }
35
+ }
36
+
37
+ /* =========================
38
+ SLIDE (from right)
39
+ ========================= */
40
+ .rtx-slide .rtx-entering {
41
+ animation: rtxSlideIn 0.25s ease forwards;
42
+ }
43
+
44
+ .rtx-slide .rtx-exit {
45
+ animation: rtxSlideOut 0.25s ease forwards;
46
+ }
47
+
48
+ @keyframes rtxSlideIn {
49
+ from { transform: translateX(100%); opacity: 0; }
50
+ to { transform: translateX(0); opacity: 1; }
51
+ }
52
+
53
+ @keyframes rtxSlideOut {
54
+ from { transform: translateX(0); opacity: 1; }
55
+ to { transform: translateX(-50%); opacity: 0; }
56
+ }
57
+
58
+ /* =========================
59
+ SCALE (zoom)
60
+ ========================= */
61
+ .rtx-scale .rtx-entering {
62
+ animation: rtxScaleIn 0.25s ease forwards;
63
+ }
64
+
65
+ .rtx-scale .rtx-exit {
66
+ animation: rtxScaleOut 0.25s ease forwards;
67
+ }
68
+
69
+ @keyframes rtxScaleIn {
70
+ from { transform: scale(0.95); opacity: 0; }
71
+ to { transform: scale(1); opacity: 1; }
72
+ }
73
+
74
+ @keyframes rtxScaleOut {
75
+ from { transform: scale(1); opacity: 1; }
76
+ to { transform: scale(0.95); opacity: 0; }
77
+ }
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "routexiz",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "A modern tree-based router for React with intent-driven navigation, Suspense-first data loading, and built-in guards, middleware, and caching.",
5
5
  "author": "Delpi.Kye",
6
6
  "license": "MIT",
7
7
  "sideEffects": false,
8
8
  "type": "module",
9
-
10
9
  "main": "./build/index.js",
11
10
  "module": "./build/index.esm.js",
12
11
  "types": "./build/index.d.ts",
@@ -15,19 +14,20 @@
15
14
  "types": "./build/index.d.ts",
16
15
  "import": "./build/index.esm.js",
17
16
  "require": "./build/index.js"
18
- }
17
+ },
18
+ "./styles.css": "./build/styles.css"
19
19
  },
20
-
21
20
  "files": [
22
21
  "build"
23
22
  ],
24
-
25
23
  "scripts": {
26
24
  "clean": "rimraf build",
27
25
  "build": "rimraf build && rollup -c",
28
26
  "watch": "rollup -c -w",
29
27
  "typecheck": "tsc --noEmit",
30
- "prepublishOnly": "npm run typecheck && npm run build"
28
+ "lint": "eslint \"src/**/*.{ts,tsx}\"",
29
+ "lint:fix": "eslint \"src/**/*.{ts,tsx}\" --fix",
30
+ "prepublishOnly": "npm run typecheck && npm run lint && npm run build"
31
31
  },
32
32
  "engines": {
33
33
  "node": ">=16"
@@ -44,7 +44,6 @@
44
44
  "type": "github",
45
45
  "url": "https://github.com/sponsors/delpikye-v"
46
46
  },
47
-
48
47
  "keywords": [
49
48
  "react",
50
49
  "router",
@@ -69,10 +68,20 @@
69
68
  "@rollup/plugin-terser": "^0.4.4",
70
69
  "@types/react": "^18.0.0",
71
70
  "@types/react-dom": "^18.0.0",
71
+ "@typescript-eslint/eslint-plugin": "^6.21.0",
72
+ "@typescript-eslint/parser": "^6.21.0",
73
+ "eslint": "^8.57.1",
74
+ "eslint-config-prettier": "^10.1.8",
75
+ "eslint-plugin-import": "^2.32.0",
76
+ "eslint-plugin-prettier": "^5.5.5",
77
+ "eslint-plugin-react": "^7.37.5",
78
+ "eslint-plugin-react-hooks": "^4.6.2",
79
+ "prettier": "^3.8.1",
72
80
  "react": "^18.0.0",
73
81
  "react-dom": "^18.0.0",
74
82
  "rimraf": "^5.0.5",
75
83
  "rollup": "^3.29.4",
84
+ "rollup-plugin-copy": "^3.5.0",
76
85
  "rollup-plugin-peer-deps-external": "^2.2.4",
77
86
  "rollup-plugin-typescript2": "^0.36.0",
78
87
  "tslib": "^2.6.2",
@@ -81,4 +90,4 @@
81
90
  "publishConfig": {
82
91
  "access": "public"
83
92
  }
84
- }
93
+ }