@mxweb/router 1.0.0 → 1.1.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/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.1.0] - 2024-12-29
9
+
10
+ ### Changed
11
+
12
+ - Changed `Route` component API from `element` prop to `children` pattern
13
+ - Route now automatically injects `navigation` prop into child components
14
+ - Added `"use client"` directive for Next.js App Router compatibility
15
+
16
+ ### Added
17
+
18
+ - `PropsWithoutNavigation` utility type for route children components
19
+
8
20
  ## [1.0.0] - 2024-12-27
9
21
 
10
22
  ### Added
@@ -21,4 +33,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
21
33
  - TypeScript support with full type definitions
22
34
  - JSDoc documentation for all public APIs
23
35
 
36
+ [1.1.0]: https://github.com/mxwebio/mxweb-router/releases/tag/v1.1.0
24
37
  [1.0.0]: https://github.com/mxwebio/mxweb-router/releases/tag/v1.0.0
package/dist/index.d.ts CHANGED
@@ -15,8 +15,8 @@
15
15
  *
16
16
  * return (
17
17
  * <Router basename="/my-feature" adapter={nextRouter}>
18
- * <Route index element={HomePage} />
19
- * <Route path="settings" element={SettingsPage} />
18
+ * <Route index><HomePage /></Route>
19
+ * <Route path="settings"><SettingsPage /></Route>
20
20
  * <Link href="/settings">Go to Settings</Link>
21
21
  * </Router>
22
22
  * );
@@ -27,8 +27,8 @@
27
27
  * function StandaloneApp() {
28
28
  * return (
29
29
  * <Router basename="/app">
30
- * <Route index element={HomePage} />
31
- * <Route path="about" element={AboutPage} />
30
+ * <Route index><HomePage /></Route>
31
+ * <Route path="about"><AboutPage /></Route>
32
32
  * </Router>
33
33
  * );
34
34
  * }
@@ -36,7 +36,7 @@
36
36
  * @packageDocumentation
37
37
  * @module @mxweb/router
38
38
  */
39
- import React, { AnchorHTMLAttributes, ComponentType, MouseEventHandler, PropsWithChildren, ReactElement, ReactNode, Ref } from "react";
39
+ import React, { AnchorHTMLAttributes, MouseEventHandler, PropsWithChildren, ReactElement, ReactNode, Ref } from "react";
40
40
  import { LiteralObject } from "@mxweb/utils";
41
41
  /**
42
42
  * Navigation interface for routing operations within the adhoc app.
@@ -77,28 +77,37 @@ export interface RouteNavigation {
77
77
  export type PropsWithNavigation<Props = {}> = Props & {
78
78
  navigation: RouteNavigation;
79
79
  };
80
+ /**
81
+ * Utility type that removes navigation prop from component props.
82
+ * Use this type for route children components before navigation is injected.
83
+ *
84
+ * @typeParam Props - The base props type for the component
85
+ */
86
+ export type PropsWithoutNavigation<Props = {}> = Omit<Props, "navigation">;
80
87
  interface RouteIndex {
81
88
  index: true;
82
89
  path?: never;
83
- element: ComponentType<PropsWithNavigation>;
90
+ children: ReactElement<PropsWithoutNavigation>;
84
91
  }
85
92
  interface RoutePath<Pathname extends string = string> {
86
93
  index?: never;
87
94
  path: Pathname;
88
- element: ComponentType<PropsWithNavigation>;
95
+ children: ReactElement<PropsWithoutNavigation>;
89
96
  }
90
97
  /**
91
98
  * Props for the Route component. Can be either an index route or a path-based route.
99
+ * The Route component accepts children instead of an element prop and automatically
100
+ * injects the navigation prop into the child component.
92
101
  *
93
102
  * @typeParam Pathname - The type of the path string for type-safe routing
94
103
  *
95
104
  * @example
96
105
  * ```tsx
97
106
  * // Index route (matches the basename exactly)
98
- * <Route index element={HomePage} />
107
+ * <Route index><HomePage /></Route>
99
108
  *
100
109
  * // Path-based route
101
- * <Route path="settings" element={SettingsPage} />
110
+ * <Route path="settings"><SettingsPage /></Route>
102
111
  * ```
103
112
  */
104
113
  export type RouteProps<Pathname extends string = string> = RouteIndex | RoutePath<Pathname>;
@@ -265,8 +274,8 @@ export interface RouterProps<Basename extends string = string> {
265
274
  *
266
275
  * return (
267
276
  * <Router basename="/my-feature" adapter={nextRouter}>
268
- * <Route index element={HomePage} />
269
- * <Route path="settings" element={SettingsPage} />
277
+ * <Route index><HomePage /></Route>
278
+ * <Route path="settings"><SettingsPage /></Route>
270
279
  * </Router>
271
280
  * );
272
281
  * }
@@ -278,8 +287,8 @@ export interface RouterProps<Basename extends string = string> {
278
287
  * function StandaloneApp() {
279
288
  * return (
280
289
  * <Router basename="/app">
281
- * <Route index element={HomePage} />
282
- * <Route path="about" element={AboutPage} />
290
+ * <Route index><HomePage /></Route>
291
+ * <Route path="about"><AboutPage /></Route>
283
292
  * </Router>
284
293
  * );
285
294
  * }
@@ -287,21 +296,27 @@ export interface RouterProps<Basename extends string = string> {
287
296
  */
288
297
  export declare function Router<Basename extends string = string>(props: PropsWithChildren<RouterProps<Basename>>): React.JSX.Element;
289
298
  /**
290
- * A route component that renders its element when the current path matches.
299
+ * A route component that renders its children when the current path matches.
300
+ * Automatically injects the `navigation` prop into the child component.
291
301
  *
292
302
  * @typeParam Pathname - The type of the path string for type-safe routing
293
303
  *
294
304
  * @example
295
305
  * ```tsx
296
306
  * // Index route - matches when pathname is empty or "/"
297
- * <Route index element={HomePage} />
307
+ * <Route index><HomePage /></Route>
298
308
  *
299
309
  * // Path route - matches when pathname starts with "settings"
300
- * <Route path="settings" element={SettingsPage} />
310
+ * <Route path="settings"><SettingsPage /></Route>
301
311
  *
302
312
  * // Nested route example
303
- * <Route path="users" element={UsersPage} />
304
- * <Route path="users/profile" element={UserProfilePage} />
313
+ * <Route path="users"><UsersPage /></Route>
314
+ * <Route path="users/profile"><UserProfilePage /></Route>
315
+ *
316
+ * // Child component receives navigation prop automatically
317
+ * function SettingsPage({ navigation }: PropsWithNavigation) {
318
+ * return <button onClick={() => navigation.pop()}>Go Back</button>;
319
+ * }
305
320
  * ```
306
321
  */
307
322
  export declare function Route<Pathname extends string = string>(props: RouteProps<Pathname>): React.JSX.Element | null;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var t=require("react"),e=require("@mxweb/store"),a=require("@mxweb/react-hooks"),s=require("@mxweb/utils");function r(t){return t&&t.__esModule?t:{default:t}}var i=r(t);class n{constructor(t,e=null){this.routerState=t,this.adapter=e}getFullPath(t){return[this.routerState.basename,t].join("/").replace(/\/\/+/g,"/")}isNextAdapter(t){return s.hasOwnProperty(t,"back")&&"function"==typeof t.back}isV5Adapter(t){return s.hasOwnProperty(t,"go")&&"function"==typeof t.go}isV6Adapter(t){return"function"==typeof t}push(t,e={}){if(this.adapter)if(this.isV6Adapter(this.adapter))this.adapter(this.getFullPath(t),{state:e});else{if(this.isNextAdapter(this.adapter)){const a=s.hasOwnProperty(e,"scroll")?{scroll:e.scroll}:void 0;return void this.adapter.push(this.getFullPath(t),a)}this.isV5Adapter(this.adapter)&&this.adapter.push(this.getFullPath(t),e)}else s.isBrowser()&&(window.history.pushState(e,"",this.getFullPath(t)),window.dispatchEvent(new PopStateEvent("popstate")))}replace(t,e={}){if(this.adapter)if(this.isV6Adapter(this.adapter))this.adapter(this.getFullPath(t),{replace:!0,state:e});else{if(this.isNextAdapter(this.adapter)){const a=s.hasOwnProperty(e,"scroll")?{scroll:e.scroll}:void 0;return void this.adapter.replace(this.getFullPath(t),a)}this.isV5Adapter(this.adapter)&&this.adapter.replace(this.getFullPath(t),e)}else s.isBrowser()&&(window.history.replaceState(e,"",this.getFullPath(t)),window.dispatchEvent(new PopStateEvent("popstate")))}pop(t=-1){this.adapter?this.isV6Adapter(this.adapter)?this.adapter(t):this.isNextAdapter(this.adapter)?-1===t?this.adapter.back():1===t&&this.adapter.forward?this.adapter.forward():s.isBrowser()&&window.history.go(t):this.isV5Adapter(this.adapter)&&this.adapter.go(t):s.isBrowser()&&window.history.go(t)}setAdapter(t){return this.adapter=t,this}setState(t){return this.routerState=t,this}}class o extends e.StoreBase{constructor(){super({basename:"",pathname:"",fullpath:""}),this.navigation=new n(this.getState())}setState(t){return super.setState(t),this.navigation.setState(this.getState()),this}setAdapter(t){return this.navigation.setAdapter(t),this}getNavigation(){return{push:this.navigation.push.bind(this.navigation),replace:this.navigation.replace.bind(this.navigation),pop:this.navigation.pop.bind(this.navigation)}}isIndex(t){return s.isNullish(t)||""===t||"/"===t}isIndexPath(){const{basename:t,pathname:e}=this.getState();return!!this.isIndex(e)||(e===t||e===`${t}/`)}isIndexRoute(t){return s.hasOwnProperty(t,"index")&&!0===t.index}isPathRoute(t){return s.hasOwnProperty(t,"path")}isMatch(t,e){return`${e}/`.startsWith(`${t.replace(/^\//,"")}/`)}snapshot(t=""){if(!s.isBrowser())return{basename:t,fullpath:"",pathname:""};const e=window.location.pathname;let a=e;const r=t.startsWith("/")?t:`/${t}`;return r&&"/"!==r&&e.startsWith(r)?a=e.slice(r.length).replace(/^\//,""):e.startsWith("/")&&(a=e.slice(1)),{basename:r,fullpath:e,pathname:a}}}const h=new o,p=t.createContext(null);function l(){const e=t.useContext(p),a=e??h.getState(),s=h.getNavigation();return{state:a,navigation:s,push:(t,e)=>s.push(t,e),replace:(t,e)=>s.replace(t,e),pop:t=>s.pop(t),isIndexPath:()=>h.isIndexPath()}}const u=t.forwardRef((e,s)=>{const{href:r,replace:n=!1,state:o,asChild:h=!1}=e,{push:p,replace:u}=l(),d=t=>{t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||0!==t.button||(t.preventDefault(),n?u(r,o):p(r,o))};if(h){const i=t.Children.only(e.children);if(!t.isValidElement(i))return null;const n=i.props;return t.cloneElement(i,{href:r,ref:a.combineRefs(s,n.ref),onClick:a.combineEvents(n.onClick,d)})}const{onClick:c,children:f,...g}=e;return i.default.createElement("a",{...g,ref:s,href:r,onClick:t=>{c?.(t),d(t)}},f)});u.displayName="Link",exports.Link=u,exports.Route=function(t){const{element:e}=t,{state:{pathname:a},navigation:s,isIndexPath:r}=l();return h.isIndexRoute(t)?r()?i.default.createElement(e,{navigation:s}):null:h.isPathRoute(t)&&h.isMatch(t.path,a)?i.default.createElement(e,{navigation:s}):null},exports.Router=function(e){const{basename:a="/",adapter:r=null,children:n}=e,[o,l]=t.useState(()=>(r&&h.setAdapter(r),h.setState(h.snapshot(a)).getState()));return t.useEffect(()=>{const t=h.onStateChange(()=>{l(t=>{const e=h.getState();return s.isEqualPrimitive(t,e)?t:e})});return h.setState(h.snapshot(a)),t},[a]),t.useEffect(()=>{r&&h.setAdapter(r)},[r]),i.default.createElement(p.Provider,{value:o},n)},exports.useRouter=l;
1
+ "use strict";var t=require("react"),e=require("@mxweb/store"),a=require("@mxweb/react-hooks"),s=require("@mxweb/utils");function r(t){return t&&t.__esModule?t:{default:t}}var i=r(t);class n{constructor(t,e=null){this.routerState=t,this.adapter=e}getFullPath(t){return[this.routerState.basename,t].join("/").replace(/\/\/+/g,"/")}isNextAdapter(t){return s.hasOwnProperty(t,"back")&&"function"==typeof t.back}isV5Adapter(t){return s.hasOwnProperty(t,"go")&&"function"==typeof t.go}isV6Adapter(t){return"function"==typeof t}push(t,e={}){if(this.adapter)if(this.isV6Adapter(this.adapter))this.adapter(this.getFullPath(t),{state:e});else{if(this.isNextAdapter(this.adapter)){const a=s.hasOwnProperty(e,"scroll")?{scroll:e.scroll}:void 0;return void this.adapter.push(this.getFullPath(t),a)}this.isV5Adapter(this.adapter)&&this.adapter.push(this.getFullPath(t),e)}else s.isBrowser()&&(window.history.pushState(e,"",this.getFullPath(t)),window.dispatchEvent(new PopStateEvent("popstate")))}replace(t,e={}){if(this.adapter)if(this.isV6Adapter(this.adapter))this.adapter(this.getFullPath(t),{replace:!0,state:e});else{if(this.isNextAdapter(this.adapter)){const a=s.hasOwnProperty(e,"scroll")?{scroll:e.scroll}:void 0;return void this.adapter.replace(this.getFullPath(t),a)}this.isV5Adapter(this.adapter)&&this.adapter.replace(this.getFullPath(t),e)}else s.isBrowser()&&(window.history.replaceState(e,"",this.getFullPath(t)),window.dispatchEvent(new PopStateEvent("popstate")))}pop(t=-1){this.adapter?this.isV6Adapter(this.adapter)?this.adapter(t):this.isNextAdapter(this.adapter)?-1===t?this.adapter.back():1===t&&this.adapter.forward?this.adapter.forward():s.isBrowser()&&window.history.go(t):this.isV5Adapter(this.adapter)&&this.adapter.go(t):s.isBrowser()&&window.history.go(t)}setAdapter(t){return this.adapter=t,this}setState(t){return this.routerState=t,this}}class o extends e.StoreBase{constructor(){super({basename:"",pathname:"",fullpath:""}),this.navigation=new n(this.getState())}setState(t){return super.setState(t),this.navigation.setState(this.getState()),this}setAdapter(t){return this.navigation.setAdapter(t),this}getNavigation(){return{push:this.navigation.push.bind(this.navigation),replace:this.navigation.replace.bind(this.navigation),pop:this.navigation.pop.bind(this.navigation)}}isIndex(t){return s.isNullish(t)||""===t||"/"===t}isIndexPath(){const{basename:t,pathname:e}=this.getState();return!!this.isIndex(e)||(e===t||e===`${t}/`)}isIndexRoute(t){return s.hasOwnProperty(t,"index")&&!0===t.index}isPathRoute(t){return s.hasOwnProperty(t,"path")}isMatch(t,e){return`${e}/`.startsWith(`${t.replace(/^\//,"")}/`)}snapshot(t=""){if(!s.isBrowser())return{basename:t,fullpath:"",pathname:""};const e=window.location.pathname;let a=e;const r=t.startsWith("/")?t:`/${t}`;return r&&"/"!==r&&e.startsWith(r)?a=e.slice(r.length).replace(/^\//,""):e.startsWith("/")&&(a=e.slice(1)),{basename:r,fullpath:e,pathname:a}}}const h=new o,l=t.createContext(null);function p(){const e=t.useContext(l),a=e??h.getState(),s=h.getNavigation();return{state:a,navigation:s,push:(t,e)=>s.push(t,e),replace:(t,e)=>s.replace(t,e),pop:t=>s.pop(t),isIndexPath:()=>h.isIndexPath()}}const u=t.forwardRef((e,s)=>{const{href:r,replace:n=!1,state:o,asChild:h=!1}=e,{push:l,replace:u}=p(),d=t=>{t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||0!==t.button||(t.preventDefault(),n?u(r,o):l(r,o))};if(h){const i=t.Children.only(e.children);if(!t.isValidElement(i))return null;const n=i.props;return t.cloneElement(i,{href:r,ref:a.combineRefs(s,n.ref),onClick:a.combineEvents(n.onClick,d)})}const{onClick:c,children:f,...g}=e;return i.default.createElement("a",{...g,ref:s,href:r,onClick:t=>{c?.(t),d(t)}},f)});u.displayName="Link",exports.Link=u,exports.Route=function(e){const{children:a}=e,{state:{pathname:s},navigation:r,isIndexPath:n}=p();return h.isIndexRoute(e)?n()?t.isValidElement(a)?t.cloneElement(a,{...a.props,navigation:r}):i.default.createElement(i.default.Fragment,null,a):null:h.isPathRoute(e)&&h.isMatch(e.path,s)?t.isValidElement(a)?t.cloneElement(a,{...a.props,navigation:r}):i.default.createElement(i.default.Fragment,null,a):null},exports.Router=function(e){const{basename:a="/",adapter:r=null,children:n}=e,[o,p]=t.useState(()=>(r&&h.setAdapter(r),h.setState(h.snapshot(a)).getState()));return t.useEffect(()=>{const t=h.onStateChange(()=>{p(t=>{const e=h.getState();return s.isEqualPrimitive(t,e)?t:e})});return h.setState(h.snapshot(a)),t},[a]),t.useEffect(()=>{r&&h.setAdapter(r)},[r]),i.default.createElement(l.Provider,{value:o},n)},exports.useRouter=p;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import t,{createContext as e,forwardRef as a,Children as s,isValidElement as i,cloneElement as r,useContext as n,useState as h,useEffect as o}from"react";import{StoreBase as p}from"@mxweb/store";import{combineEvents as l,combineRefs as d}from"@mxweb/react-hooks";import{isNullish as u,hasOwnProperty as c,isBrowser as g,isEqualPrimitive as f}from"@mxweb/utils";class m{constructor(t,e=null){this.routerState=t,this.adapter=e}getFullPath(t){return[this.routerState.basename,t].join("/").replace(/\/\/+/g,"/")}isNextAdapter(t){return c(t,"back")&&"function"==typeof t.back}isV5Adapter(t){return c(t,"go")&&"function"==typeof t.go}isV6Adapter(t){return"function"==typeof t}push(t,e={}){if(this.adapter)if(this.isV6Adapter(this.adapter))this.adapter(this.getFullPath(t),{state:e});else{if(this.isNextAdapter(this.adapter)){const a=c(e,"scroll")?{scroll:e.scroll}:void 0;return void this.adapter.push(this.getFullPath(t),a)}this.isV5Adapter(this.adapter)&&this.adapter.push(this.getFullPath(t),e)}else g()&&(window.history.pushState(e,"",this.getFullPath(t)),window.dispatchEvent(new PopStateEvent("popstate")))}replace(t,e={}){if(this.adapter)if(this.isV6Adapter(this.adapter))this.adapter(this.getFullPath(t),{replace:!0,state:e});else{if(this.isNextAdapter(this.adapter)){const a=c(e,"scroll")?{scroll:e.scroll}:void 0;return void this.adapter.replace(this.getFullPath(t),a)}this.isV5Adapter(this.adapter)&&this.adapter.replace(this.getFullPath(t),e)}else g()&&(window.history.replaceState(e,"",this.getFullPath(t)),window.dispatchEvent(new PopStateEvent("popstate")))}pop(t=-1){this.adapter?this.isV6Adapter(this.adapter)?this.adapter(t):this.isNextAdapter(this.adapter)?-1===t?this.adapter.back():1===t&&this.adapter.forward?this.adapter.forward():g()&&window.history.go(t):this.isV5Adapter(this.adapter)&&this.adapter.go(t):g()&&window.history.go(t)}setAdapter(t){return this.adapter=t,this}setState(t){return this.routerState=t,this}}const v=new class extends p{constructor(){super({basename:"",pathname:"",fullpath:""}),this.navigation=new m(this.getState())}setState(t){return super.setState(t),this.navigation.setState(this.getState()),this}setAdapter(t){return this.navigation.setAdapter(t),this}getNavigation(){return{push:this.navigation.push.bind(this.navigation),replace:this.navigation.replace.bind(this.navigation),pop:this.navigation.pop.bind(this.navigation)}}isIndex(t){return u(t)||""===t||"/"===t}isIndexPath(){const{basename:t,pathname:e}=this.getState();return!!this.isIndex(e)||(e===t||e===`${t}/`)}isIndexRoute(t){return c(t,"index")&&!0===t.index}isPathRoute(t){return c(t,"path")}isMatch(t,e){return`${e}/`.startsWith(`${t.replace(/^\//,"")}/`)}snapshot(t=""){if(!g())return{basename:t,fullpath:"",pathname:""};const e=window.location.pathname;let a=e;const s=t.startsWith("/")?t:`/${t}`;return s&&"/"!==s&&e.startsWith(s)?a=e.slice(s.length).replace(/^\//,""):e.startsWith("/")&&(a=e.slice(1)),{basename:s,fullpath:e,pathname:a}}},w=e(null);function S(){const t=n(w),e=t??v.getState(),a=v.getNavigation();return{state:e,navigation:a,push:(t,e)=>a.push(t,e),replace:(t,e)=>a.replace(t,e),pop:t=>a.pop(t),isIndexPath:()=>v.isIndexPath()}}function x(e){const{basename:a="/",adapter:s=null,children:i}=e,[r,n]=h(()=>(s&&v.setAdapter(s),v.setState(v.snapshot(a)).getState()));return o(()=>{const t=v.onStateChange(()=>{n(t=>{const e=v.getState();return f(t,e)?t:e})});return v.setState(v.snapshot(a)),t},[a]),o(()=>{s&&v.setAdapter(s)},[s]),t.createElement(w.Provider,{value:r},i)}function P(e){const{element:a}=e,{state:{pathname:s},navigation:i,isIndexPath:r}=S();return v.isIndexRoute(e)?r()?t.createElement(a,{navigation:i}):null:v.isPathRoute(e)&&v.isMatch(e.path,s)?t.createElement(a,{navigation:i}):null}const A=a((e,a)=>{const{href:n,replace:h=!1,state:o,asChild:p=!1}=e,{push:u,replace:c}=S(),g=t=>{t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||0!==t.button||(t.preventDefault(),h?c(n,o):u(n,o))};if(p){const t=s.only(e.children);if(!i(t))return null;const h=t.props;return r(t,{href:n,ref:d(a,h.ref),onClick:l(h.onClick,g)})}const{onClick:f,children:m,...v}=e;return t.createElement("a",{...v,ref:a,href:n,onClick:t=>{f?.(t),g(t)}},m)});A.displayName="Link";export{A as Link,P as Route,x as Router,S as useRouter};
1
+ import t,{createContext as e,forwardRef as a,Children as s,isValidElement as i,cloneElement as r,useContext as n,useState as h,useEffect as o}from"react";import{StoreBase as p}from"@mxweb/store";import{combineEvents as l,combineRefs as d}from"@mxweb/react-hooks";import{isNullish as u,hasOwnProperty as c,isBrowser as g,isEqualPrimitive as f}from"@mxweb/utils";class m{constructor(t,e=null){this.routerState=t,this.adapter=e}getFullPath(t){return[this.routerState.basename,t].join("/").replace(/\/\/+/g,"/")}isNextAdapter(t){return c(t,"back")&&"function"==typeof t.back}isV5Adapter(t){return c(t,"go")&&"function"==typeof t.go}isV6Adapter(t){return"function"==typeof t}push(t,e={}){if(this.adapter)if(this.isV6Adapter(this.adapter))this.adapter(this.getFullPath(t),{state:e});else{if(this.isNextAdapter(this.adapter)){const a=c(e,"scroll")?{scroll:e.scroll}:void 0;return void this.adapter.push(this.getFullPath(t),a)}this.isV5Adapter(this.adapter)&&this.adapter.push(this.getFullPath(t),e)}else g()&&(window.history.pushState(e,"",this.getFullPath(t)),window.dispatchEvent(new PopStateEvent("popstate")))}replace(t,e={}){if(this.adapter)if(this.isV6Adapter(this.adapter))this.adapter(this.getFullPath(t),{replace:!0,state:e});else{if(this.isNextAdapter(this.adapter)){const a=c(e,"scroll")?{scroll:e.scroll}:void 0;return void this.adapter.replace(this.getFullPath(t),a)}this.isV5Adapter(this.adapter)&&this.adapter.replace(this.getFullPath(t),e)}else g()&&(window.history.replaceState(e,"",this.getFullPath(t)),window.dispatchEvent(new PopStateEvent("popstate")))}pop(t=-1){this.adapter?this.isV6Adapter(this.adapter)?this.adapter(t):this.isNextAdapter(this.adapter)?-1===t?this.adapter.back():1===t&&this.adapter.forward?this.adapter.forward():g()&&window.history.go(t):this.isV5Adapter(this.adapter)&&this.adapter.go(t):g()&&window.history.go(t)}setAdapter(t){return this.adapter=t,this}setState(t){return this.routerState=t,this}}const v=new class extends p{constructor(){super({basename:"",pathname:"",fullpath:""}),this.navigation=new m(this.getState())}setState(t){return super.setState(t),this.navigation.setState(this.getState()),this}setAdapter(t){return this.navigation.setAdapter(t),this}getNavigation(){return{push:this.navigation.push.bind(this.navigation),replace:this.navigation.replace.bind(this.navigation),pop:this.navigation.pop.bind(this.navigation)}}isIndex(t){return u(t)||""===t||"/"===t}isIndexPath(){const{basename:t,pathname:e}=this.getState();return!!this.isIndex(e)||(e===t||e===`${t}/`)}isIndexRoute(t){return c(t,"index")&&!0===t.index}isPathRoute(t){return c(t,"path")}isMatch(t,e){return`${e}/`.startsWith(`${t.replace(/^\//,"")}/`)}snapshot(t=""){if(!g())return{basename:t,fullpath:"",pathname:""};const e=window.location.pathname;let a=e;const s=t.startsWith("/")?t:`/${t}`;return s&&"/"!==s&&e.startsWith(s)?a=e.slice(s.length).replace(/^\//,""):e.startsWith("/")&&(a=e.slice(1)),{basename:s,fullpath:e,pathname:a}}},w=e(null);function S(){const t=n(w),e=t??v.getState(),a=v.getNavigation();return{state:e,navigation:a,push:(t,e)=>a.push(t,e),replace:(t,e)=>a.replace(t,e),pop:t=>a.pop(t),isIndexPath:()=>v.isIndexPath()}}function x(e){const{basename:a="/",adapter:s=null,children:i}=e,[r,n]=h(()=>(s&&v.setAdapter(s),v.setState(v.snapshot(a)).getState()));return o(()=>{const t=v.onStateChange(()=>{n(t=>{const e=v.getState();return f(t,e)?t:e})});return v.setState(v.snapshot(a)),t},[a]),o(()=>{s&&v.setAdapter(s)},[s]),t.createElement(w.Provider,{value:r},i)}function P(e){const{children:a}=e,{state:{pathname:s},navigation:n,isIndexPath:h}=S();return v.isIndexRoute(e)?h()?i(a)?r(a,{...a.props,navigation:n}):t.createElement(t.Fragment,null,a):null:v.isPathRoute(e)&&v.isMatch(e.path,s)?i(a)?r(a,{...a.props,navigation:n}):t.createElement(t.Fragment,null,a):null}const A=a((e,a)=>{const{href:n,replace:h=!1,state:o,asChild:p=!1}=e,{push:u,replace:c}=S(),g=t=>{t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||0!==t.button||(t.preventDefault(),h?c(n,o):u(n,o))};if(p){const t=s.only(e.children);if(!i(t))return null;const h=t.props;return r(t,{href:n,ref:d(a,h.ref),onClick:l(h.onClick,g)})}const{onClick:f,children:m,...v}=e;return t.createElement("a",{...v,ref:a,href:n,onClick:t=>{f?.(t),g(t)}},m)});A.displayName="Link";export{A as Link,P as Route,x as Router,S as useRouter};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mxweb/router",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A lightweight router for Next.js App Router adhoc features with built-in and adapter-based navigation support",
5
5
  "keywords": [
6
6
  "react",