intentx-react-router 0.2.0 → 1.0.0-z
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 +169 -86
- package/build/core/intentCore.d.ts +13 -3
- package/build/index.esm.js +1 -1
- package/build/index.js +1 -1
- package/build/router/IntentLink.d.ts +3 -2
- package/build/router/IntentRoute.d.ts +5 -2
- package/build/router/IntentRouter.d.ts +5 -0
- package/build/router/index.d.ts +2 -1
- package/build/router/useIntentRouter.d.ts +6 -0
- package/package.json +1 -7
- package/build/router/useIntent.d.ts +0 -5
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ navigateIntent("edit-user", 42)
|
|
|
39
39
|
# Installation
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
npm install intentx-react-router
|
|
42
|
+
npm install intentx-react-router react-router@^7 react-router-dom@^7
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
## Recommended Versions
|
|
@@ -48,7 +48,6 @@ npm install intentx-react-router eventbus-z react-router@^7 react-router-dom@^7
|
|
|
48
48
|
- react-dom: >=18
|
|
49
49
|
- react-router: >=7
|
|
50
50
|
- react-router-dom: >=7
|
|
51
|
-
- eventbus-z: ^2.4.0
|
|
52
51
|
|
|
53
52
|
---
|
|
54
53
|
|
|
@@ -81,51 +80,15 @@ export function RouterBinder() {
|
|
|
81
80
|
return null
|
|
82
81
|
}
|
|
83
82
|
```
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
## 3. IntentLink
|
|
88
|
-
|
|
89
|
-
```ts
|
|
90
|
-
import React from "react"
|
|
91
|
-
import { Link } from "react-router-dom"
|
|
92
|
-
import { generatePathFromIntent } from "intentx-react-router"
|
|
93
|
-
|
|
94
|
-
export function IntentLink({ intent, params, query, children }) {
|
|
95
|
-
let path = generatePathFromIntent(intent, params)
|
|
96
|
-
if (query) {
|
|
97
|
-
const qs = new URLSearchParams(query).toString()
|
|
98
|
-
if (qs) path += "?" + qs
|
|
99
|
-
}
|
|
100
|
-
return <Link to={path}>{children}</Link>
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## 4. IntentRoute
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
import React from "react"
|
|
112
|
-
import { Route } from "react-router-dom"
|
|
113
|
-
import { generatePathFromIntent } from "intentx-react-router"
|
|
114
|
-
|
|
115
|
-
export function IntentRoute({ intent, component: Component }) {
|
|
116
|
-
const path = generatePathFromIntent(intent)
|
|
117
|
-
return <Route path={path} render={(props) => <Component {...props} />} />
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
83
|
---
|
|
122
84
|
|
|
123
|
-
##
|
|
85
|
+
## 3. Use in App
|
|
124
86
|
|
|
125
87
|
```ts
|
|
126
88
|
import React from "react"
|
|
127
|
-
import { BrowserRouter,
|
|
128
|
-
import {
|
|
89
|
+
import { BrowserRouter, Routes } from "react-router-dom"
|
|
90
|
+
import { IntentLink, IntentRoute } from "intentx-react-router"
|
|
91
|
+
import { RouterBinder } from "./router"
|
|
129
92
|
|
|
130
93
|
import { UserPage } from "./pages/UserPage"
|
|
131
94
|
import { EditUserPage } from "./pages/EditUserPage"
|
|
@@ -142,39 +105,30 @@ export default function App() {
|
|
|
142
105
|
| <IntentLink intent="checkout">Checkout</IntentLink>
|
|
143
106
|
</nav>
|
|
144
107
|
|
|
145
|
-
<
|
|
108
|
+
<Routes>
|
|
146
109
|
<IntentRoute intent="view-user" component={UserPage} />
|
|
147
110
|
<IntentRoute intent="edit-user" component={EditUserPage} />
|
|
148
111
|
<IntentRoute intent="checkout" component={CheckoutPage} />
|
|
149
|
-
</
|
|
112
|
+
</Routes>
|
|
150
113
|
</BrowserRouter>
|
|
151
114
|
)
|
|
152
115
|
}
|
|
153
116
|
```
|
|
154
117
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
## 6. useIntent Hook
|
|
118
|
+
## 4. IntentRouter
|
|
158
119
|
|
|
159
120
|
```ts
|
|
160
|
-
import {
|
|
161
|
-
import {
|
|
162
|
-
|
|
163
|
-
export function
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
return {
|
|
174
|
-
intent: resolved?.intent ?? null,
|
|
175
|
-
params: resolved?.params ?? {},
|
|
176
|
-
query
|
|
177
|
-
}
|
|
121
|
+
import { IntentRouter } from "intentx-react-router"
|
|
122
|
+
import { Routes } from "react-router-dom"
|
|
123
|
+
|
|
124
|
+
export default function App() {
|
|
125
|
+
return (
|
|
126
|
+
<IntentRouter>
|
|
127
|
+
<Routes>
|
|
128
|
+
<IntentRoute intent="checkout" component={CheckoutPage} />
|
|
129
|
+
</Routes>
|
|
130
|
+
</IntentRouter>
|
|
131
|
+
)
|
|
178
132
|
}
|
|
179
133
|
```
|
|
180
134
|
|
|
@@ -227,6 +181,82 @@ Result:
|
|
|
227
181
|
|
|
228
182
|
---
|
|
229
183
|
|
|
184
|
+
## Query params
|
|
185
|
+
|
|
186
|
+
Intent navigation also supports query parameters.
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
navigateIntent("view-user", {
|
|
190
|
+
userId: 1,
|
|
191
|
+
page: 2
|
|
192
|
+
}, {
|
|
193
|
+
tab: "activity",
|
|
194
|
+
filter: "recent"
|
|
195
|
+
})
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
/users/1/name/2?tab=activity&filter=recent
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
Query arrays are also supported:
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
navigateIntent("view-user", [1,2], {
|
|
208
|
+
tags: ["admin", "vip"]
|
|
209
|
+
})
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Result:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
/users/1/name/2?tags=admin&tags=vip
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Advanced Navigation
|
|
221
|
+
|
|
222
|
+
options, fallback, microFE
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import React from "react"
|
|
226
|
+
import { useNavigateIntent, setFallbackPath, setSharedBus } from "intentx-react-router"
|
|
227
|
+
import { createEventBus } from "eventbus-z"
|
|
228
|
+
|
|
229
|
+
// Micro-frontend shared bus
|
|
230
|
+
const externalBus = createEventBus()
|
|
231
|
+
setSharedBus(externalBus)
|
|
232
|
+
|
|
233
|
+
setFallbackPath("/not-found")
|
|
234
|
+
|
|
235
|
+
function Dashboard() {
|
|
236
|
+
const navigateIntent = useNavigateIntent()
|
|
237
|
+
|
|
238
|
+
const handleCheckout = () => {
|
|
239
|
+
navigateIntent("checkout", undefined, { ref: "dashboard" }, { replace: true, scrollTop: true })
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<div>
|
|
244
|
+
<h1>Dashboard</h1>
|
|
245
|
+
<button onClick={handleCheckout}>Go to Checkout</button>
|
|
246
|
+
<button
|
|
247
|
+
onClick={() => {
|
|
248
|
+
navigateIntent("unknown-intent")
|
|
249
|
+
}}
|
|
250
|
+
>
|
|
251
|
+
Unknown Intent
|
|
252
|
+
</button>
|
|
253
|
+
</div>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
230
260
|
# React Helpers
|
|
231
261
|
|
|
232
262
|
## useNavigateIntent
|
|
@@ -252,7 +282,7 @@ function Button() {
|
|
|
252
282
|
``` tsx
|
|
253
283
|
import { IntentLink } from "intentx-react-router"
|
|
254
284
|
|
|
255
|
-
<IntentLink intent="view-user" params={[1,2]}>
|
|
285
|
+
<IntentLink intent="view-user" params={[1,2]} query={{ tab: "profile" }}>
|
|
256
286
|
Open User
|
|
257
287
|
</IntentLink>
|
|
258
288
|
```
|
|
@@ -260,20 +290,20 @@ import { IntentLink } from "intentx-react-router"
|
|
|
260
290
|
Equivalent to:
|
|
261
291
|
|
|
262
292
|
```ts
|
|
263
|
-
<Link to="/users/1/name/2" />
|
|
293
|
+
<Link to="/users/1/name/2?tab=profile" />
|
|
264
294
|
```
|
|
265
295
|
|
|
266
296
|
---
|
|
267
297
|
|
|
268
|
-
##
|
|
298
|
+
## useIntentRouter
|
|
269
299
|
|
|
270
300
|
Reads intent + params from URL.
|
|
271
301
|
|
|
272
302
|
``` ts
|
|
273
|
-
import {
|
|
303
|
+
import { useIntentRouter } from "intentx-react-router"
|
|
274
304
|
|
|
275
305
|
function Page() {
|
|
276
|
-
const { intent, params } =
|
|
306
|
+
const { intent, params } = useIntentRouter()
|
|
277
307
|
|
|
278
308
|
console.log(intent)
|
|
279
309
|
console.log(params)
|
|
@@ -285,13 +315,13 @@ function Page() {
|
|
|
285
315
|
Example result:
|
|
286
316
|
|
|
287
317
|
```ts
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
318
|
+
{
|
|
319
|
+
intent: "view-user",
|
|
320
|
+
params: {
|
|
321
|
+
userId: "1",
|
|
322
|
+
page: "2"
|
|
294
323
|
}
|
|
324
|
+
}
|
|
295
325
|
```
|
|
296
326
|
|
|
297
327
|
---
|
|
@@ -362,34 +392,86 @@ preloadIntent("checkout")
|
|
|
362
392
|
Generate path manually.
|
|
363
393
|
|
|
364
394
|
``` ts
|
|
365
|
-
generatePathFromIntent("view-user", [1,2]
|
|
395
|
+
generatePathFromIntent("view-user", [1,2], {
|
|
396
|
+
tab: "activity"
|
|
397
|
+
})
|
|
366
398
|
```
|
|
367
399
|
|
|
368
400
|
Result:
|
|
369
401
|
```ts
|
|
370
|
-
/users/1/name/2
|
|
402
|
+
/users/1/name/2?tab=activity
|
|
371
403
|
```
|
|
372
404
|
---
|
|
373
405
|
|
|
374
406
|
# Resolve Intent From URL
|
|
375
407
|
|
|
376
408
|
``` ts
|
|
377
|
-
resolveIntentFromUrl("/users/1/name/2")
|
|
409
|
+
resolveIntentFromUrl("/users/1/name/2?tab=activity")
|
|
378
410
|
```
|
|
379
411
|
|
|
380
412
|
Result:
|
|
381
413
|
```ts
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
414
|
+
{
|
|
415
|
+
intent: "view-user",
|
|
416
|
+
params: {
|
|
417
|
+
userId: "1",
|
|
418
|
+
page: "2"
|
|
419
|
+
},
|
|
420
|
+
query: {
|
|
421
|
+
tab: "activity"
|
|
422
|
+
}
|
|
423
|
+
}
|
|
389
424
|
```
|
|
390
425
|
|
|
391
426
|
---
|
|
392
427
|
|
|
428
|
+
# Event-Driven Navigation
|
|
429
|
+
|
|
430
|
+
Example
|
|
431
|
+
|
|
432
|
+
```ts
|
|
433
|
+
import { createEventBus } from "eventbus-z"
|
|
434
|
+
import { navigateIntent } from "intentx-react-router"
|
|
435
|
+
|
|
436
|
+
const bus = createEventBus()
|
|
437
|
+
|
|
438
|
+
// Somewhere in your app
|
|
439
|
+
bus.$on("CHECKOUT_REQUESTED", (userId) => {
|
|
440
|
+
navigateIntent("checkout", { userId })
|
|
441
|
+
})
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Trigger navigation from anywhere:
|
|
445
|
+
```ts
|
|
446
|
+
bus.$emit("CHECKOUT_REQUESTED", 42)
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Result:
|
|
450
|
+
```ts
|
|
451
|
+
/checkout?userId=42
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Why this is powerful
|
|
455
|
+
- Navigation becomes decoupled from UI components.
|
|
456
|
+
|
|
457
|
+
Instead of:
|
|
458
|
+
|
|
459
|
+
```ts
|
|
460
|
+
<button onClick={() => navigate("/checkout")} />
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
You can emit a domain event:
|
|
464
|
+
```ts
|
|
465
|
+
bus.$emit("CHECKOUT_REQUESTED")
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
This allows:
|
|
469
|
+
- UI-agnostic navigation
|
|
470
|
+
- better architecture in large apps
|
|
471
|
+
- seamless micro-frontend communication
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
393
475
|
# Comparison
|
|
394
476
|
|
|
395
477
|
| Criteria | intent-router | React Router |
|
|
@@ -400,6 +482,7 @@ Result:
|
|
|
400
482
|
| Micro-frontend friendly | ✅ | ⚠️ |
|
|
401
483
|
| Reverse routing | ✅ | ❌ |
|
|
402
484
|
| Type-safe intent params | ✅ | ⚠️ |
|
|
485
|
+
| Query support | ✅ | ⚠️ |
|
|
403
486
|
|
|
404
487
|
---
|
|
405
488
|
|
|
@@ -1,17 +1,27 @@
|
|
|
1
|
+
import type { GlobalBus } from "eventbus-z";
|
|
1
2
|
type IntentMap = Record<string, string>;
|
|
2
3
|
type GuardFn = (intent: string, params: any) => boolean | string | void;
|
|
3
|
-
type NavigateFn = (path: string
|
|
4
|
+
type NavigateFn = (path: string, options?: {
|
|
5
|
+
replace?: boolean;
|
|
6
|
+
scrollTop?: boolean;
|
|
7
|
+
}) => void;
|
|
4
8
|
type PreloadFn = () => Promise<any> | void;
|
|
5
9
|
export declare function createIntentRouter(map: IntentMap): void;
|
|
6
10
|
export declare function bindNavigate(fn: NavigateFn): void;
|
|
7
|
-
export declare function
|
|
11
|
+
export declare function setFallbackPath(path: string): void;
|
|
12
|
+
export declare function navigateIntent<P = any>(intent: string, params?: P | any[] | string | number, query?: Record<string, any>, options?: {
|
|
13
|
+
replace?: boolean;
|
|
14
|
+
scrollTop?: boolean;
|
|
15
|
+
}): void;
|
|
8
16
|
export declare function addIntentGuard(fn: GuardFn): void;
|
|
9
17
|
export declare function addIntentGuardFor(intent: string, fn: GuardFn): void;
|
|
10
18
|
export declare function addIntentPreload(intent: string, fn: PreloadFn): void;
|
|
11
19
|
export declare function preloadIntent(intent: string): void;
|
|
12
|
-
export declare function generatePathFromIntent(intent: string, params?: Record<string, any> | any[] | string | number): string;
|
|
20
|
+
export declare function generatePathFromIntent(intent: string, params?: Record<string, any> | any[] | string | number, query?: Record<string, any>): string;
|
|
13
21
|
export declare function resolveIntentFromUrl(url: string): {
|
|
14
22
|
intent: string;
|
|
15
23
|
params: Record<string, any>;
|
|
24
|
+
query: Record<string, any>;
|
|
16
25
|
} | null;
|
|
26
|
+
export declare function setSharedBus(externalBus: GlobalBus): void;
|
|
17
27
|
export {};
|
package/build/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createEventBus as n}from"eventbus-z";import{jsx as t}from"react/jsx-runtime";import{Link as
|
|
1
|
+
import{createEventBus as n}from"eventbus-z";import{jsx as t,Fragment as r,jsxs as o}from"react/jsx-runtime";import{Link as e,useLocation as i,useNavigate as c,BrowserRouter as u}from"react-router-dom";import{useEffect as a}from"react";const s=n();let f=null;let l={},p=null,m=!1;const d=[],h={},y={};let T=null;const A="undefined"!=typeof window&&!0===window.__INTENT_ROUTER_DEBUG__;function E(...n){A&&console.log("[IntentRouter]",...n)}function N(n){if(!n)return"";const t=new URLSearchParams;for(const r in n){const o=n[r];null!=o&&(Array.isArray(o)?o.forEach(n=>t.append(r,String(n))):t.append(r,String(o)))}const r=t.toString();return r?`?${r}`:""}function g(n){l=n,E("intent map loaded",n)}function w(n){p=n,m||(s.$onMultiple("INTENT_NAVIGATE",$),m=!0)}function I(n){T=n}function _(n,t,r,o){E("emit",n,{params:t,query:r});const e={intent:n,params:t,query:r,...o?{options:o}:{}};s.$emit("INTENT_NAVIGATE",e),f&&f.$emit("INTENT_NAVIGATE",e)}function v(n){d.push(n)}function R(n,t){h[n]||(h[n]=[]),h[n].push(t)}function q(n,t){y[n]||(y[n]=[]),y[n].push(t)}function U(n){G(n)}async function $(n){const t=l[n.intent];if(E("receive",n.intent,n),!t)return T&&p?.(T),void console.warn("Unknown intent:",n.intent);const r=S(t,n.params);for(const t of d){const o=await t(n.intent,r);if(!1===o)return;if("string"==typeof o)return void p?.(o,n.options)}const o=h[n.intent]||[];for(const t of o){const o=await t(n.intent,r);if(!1===o)return;if("string"==typeof o)return void p?.(o,n.options)}G(n.intent);const e=V(t,r)+N(n.query);E("navigate",{intent:n.intent,params:r,query:n.query,url:e}),p?.(e,n.options),f&&f.$emit("INTENT_NAVIGATE",n)}function G(n){const t=y[n];if(t)for(const n of t)try{const t=n();t instanceof Promise&&t.catch(()=>{})}catch{}}function S(n,t){const r=function(n){const t=[];return n.replace(/:([A-Za-z0-9_]+)/g,(n,r)=>(t.push(r),"")),t}(n);if(!t)return{};if(Array.isArray(t)){const n={};return r.forEach((r,o)=>{void 0===t[o]&&console.warn(`Missing param "${r}"`),n[r]=t[o]}),n}return"object"==typeof t?t:1===r.length?{[r[0]]:t}:{}}function V(n,t){return n.replace(/:([A-Za-z0-9_]+)/g,(n,r)=>{const o=t[r];return void 0!==o?encodeURIComponent(o):""})}function z(n,t,r){const o=l[n];if(!o)return"";return V(o,S(o,t))+N(r)}function P(n){const[t,r]=n.split("?"),o=function(n){if(!n)return{};const t=new URLSearchParams(n),r={};return t.forEach((n,t)=>{r[t]?Array.isArray(r[t])?r[t].push(n):r[t]=[r[t],n]:r[t]=n}),r}(r);for(const n in l){const r=l[n],e=[],i=new RegExp("^"+r.replace(/:([A-Za-z0-9_]+)/g,(n,t)=>(e.push(t),"([^/]+)"))+"$"),c=t.match(i);if(!c)continue;const u={};return e.forEach((n,t)=>{u[n]=decodeURIComponent(c[t+1])}),{intent:n,params:u,query:o}}return null}function b(n){f=n,f.$onMultiple("INTENT_NAVIGATE",$)}function k({intent:n,params:r,query:o,children:i,replace:c,scrollTop:u}){const a=z(n,r,o);return t(e,{to:a,replace:c,onClick:()=>{u&&window.scrollTo(0,0)},children:i})}function x(){const n=i(),t=P(n.pathname),r=new URLSearchParams(n.search),o={};return r.forEach((n,t)=>{o[t]=n}),{intent:t?.intent??null,params:t?.params??{},query:o}}function C({intent:n,component:o,fallback:e=null,loading:i,guard:c}){const u=x();return u?u.intent!==n?null:c&&!c(u)?t(r,{children:e}):t(o,{...u}):i??null}function L({navigate:n}){const t=c();return w(n||t),null}function M({children:n}){return o(u,{children:[t(L,{}),n]})}function Z(){const n=c();return a(()=>{w(n)},[n]),_}export{k as IntentLink,C as IntentRoute,M as IntentRouter,L as RouterBinder,v as addIntentGuard,R as addIntentGuardFor,q as addIntentPreload,w as bindNavigate,g as createIntentRouter,z as generatePathFromIntent,_ as navigateIntent,U as preloadIntent,P as resolveIntentFromUrl,I as setFallbackPath,b as setSharedBus,x as useIntentRouter,Z as useNavigateIntent};
|
package/build/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var n=require("eventbus-z"),t=require("react/jsx-runtime"),e=require("react-router-dom"),r=require("react");const o=n.createEventBus()
|
|
1
|
+
"use strict";var n=require("eventbus-z"),t=require("react/jsx-runtime"),e=require("react-router-dom"),r=require("react");const o=n.createEventBus();let i=null;let s={},c=null,u=!1;const a=[],p={},f={};let l=null;const d="undefined"!=typeof window&&!0===window.__INTENT_ROUTER_DEBUG__;function h(...n){d&&console.log("[IntentRouter]",...n)}function m(n){if(!n)return"";const t=new URLSearchParams;for(const e in n){const r=n[e];null!=r&&(Array.isArray(r)?r.forEach(n=>t.append(e,String(n))):t.append(e,String(r)))}const e=t.toString();return e?`?${e}`:""}function I(n){c=n,u||(o.$onMultiple("INTENT_NAVIGATE",g),u=!0)}function x(n,t,e,r){h("emit",n,{params:t,query:e});const s={intent:n,params:t,query:e,...r?{options:r}:{}};o.$emit("INTENT_NAVIGATE",s),i&&i.$emit("INTENT_NAVIGATE",s)}async function g(n){const t=s[n.intent];if(h("receive",n.intent,n),!t)return l&&c?.(l),void console.warn("Unknown intent:",n.intent);const e=N(t,n.params);for(const t of a){const r=await t(n.intent,e);if(!1===r)return;if("string"==typeof r)return void c?.(r,n.options)}const r=p[n.intent]||[];for(const t of r){const r=await t(n.intent,e);if(!1===r)return;if("string"==typeof r)return void c?.(r,n.options)}E(n.intent);const o=y(t,e)+m(n.query);h("navigate",{intent:n.intent,params:e,query:n.query,url:o}),c?.(o,n.options),i&&i.$emit("INTENT_NAVIGATE",n)}function E(n){const t=f[n];if(t)for(const n of t)try{const t=n();t instanceof Promise&&t.catch(()=>{})}catch{}}function N(n,t){const e=function(n){const t=[];return n.replace(/:([A-Za-z0-9_]+)/g,(n,e)=>(t.push(e),"")),t}(n);if(!t)return{};if(Array.isArray(t)){const n={};return e.forEach((e,r)=>{void 0===t[r]&&console.warn(`Missing param "${e}"`),n[e]=t[r]}),n}return"object"==typeof t?t:1===e.length?{[e[0]]:t}:{}}function y(n,t){return n.replace(/:([A-Za-z0-9_]+)/g,(n,e)=>{const r=t[e];return void 0!==r?encodeURIComponent(r):""})}function T(n,t,e){const r=s[n];if(!r)return"";return y(r,N(r,t))+m(e)}function A(n){const[t,e]=n.split("?"),r=function(n){if(!n)return{};const t=new URLSearchParams(n),e={};return t.forEach((n,t)=>{e[t]?Array.isArray(e[t])?e[t].push(n):e[t]=[e[t],n]:e[t]=n}),e}(e);for(const n in s){const e=s[n],o=[],i=new RegExp("^"+e.replace(/:([A-Za-z0-9_]+)/g,(n,t)=>(o.push(t),"([^/]+)"))+"$"),c=t.match(i);if(!c)continue;const u={};return o.forEach((n,t)=>{u[n]=decodeURIComponent(c[t+1])}),{intent:n,params:u,query:r}}return null}function v(){const n=e.useLocation(),t=A(n.pathname),r=new URLSearchParams(n.search),o={};return r.forEach((n,t)=>{o[t]=n}),{intent:t?.intent??null,params:t?.params??{},query:o}}function w({navigate:n}){const t=e.useNavigate();return I(n||t),null}exports.IntentLink=function({intent:n,params:r,query:o,children:i,replace:s,scrollTop:c}){const u=T(n,r,o);return t.jsx(e.Link,{to:u,replace:s,onClick:()=>{c&&window.scrollTo(0,0)},children:i})},exports.IntentRoute=function({intent:n,component:e,fallback:r=null,loading:o,guard:i}){const s=v();return s?s.intent!==n?null:i&&!i(s)?t.jsx(t.Fragment,{children:r}):t.jsx(e,{...s}):o??null},exports.IntentRouter=function({children:n}){return t.jsxs(e.BrowserRouter,{children:[t.jsx(w,{}),n]})},exports.RouterBinder=w,exports.addIntentGuard=function(n){a.push(n)},exports.addIntentGuardFor=function(n,t){p[n]||(p[n]=[]),p[n].push(t)},exports.addIntentPreload=function(n,t){f[n]||(f[n]=[]),f[n].push(t)},exports.bindNavigate=I,exports.createIntentRouter=function(n){s=n,h("intent map loaded",n)},exports.generatePathFromIntent=T,exports.navigateIntent=x,exports.preloadIntent=function(n){E(n)},exports.resolveIntentFromUrl=A,exports.setFallbackPath=function(n){l=n},exports.setSharedBus=function(n){i=n,i.$onMultiple("INTENT_NAVIGATE",g)},exports.useIntentRouter=v,exports.useNavigateIntent=function(){const n=e.useNavigate();return r.useEffect(()=>{I(n)},[n]),x};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
type Props = {
|
|
3
2
|
intent: string;
|
|
4
3
|
params?: any;
|
|
5
4
|
query?: Record<string, any>;
|
|
6
5
|
children: React.ReactNode;
|
|
6
|
+
replace?: boolean;
|
|
7
|
+
scrollTop?: boolean;
|
|
7
8
|
};
|
|
8
|
-
export declare function IntentLink({ intent, params, query, children }: Props): JSX.Element;
|
|
9
|
+
export declare function IntentLink({ intent, params, query, children, replace, scrollTop }: Props): JSX.Element;
|
|
9
10
|
export {};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { IntentData } from "./useIntentRouter";
|
|
2
2
|
type Props = {
|
|
3
3
|
intent: string;
|
|
4
4
|
component: React.ComponentType<any>;
|
|
5
|
+
fallback?: React.ReactNode;
|
|
6
|
+
loading?: React.ReactNode;
|
|
7
|
+
guard?: (data: IntentData) => boolean;
|
|
5
8
|
};
|
|
6
|
-
export declare function IntentRoute({ intent, component: Component }: Props):
|
|
9
|
+
export declare function IntentRoute({ intent, component: Component, fallback, loading, guard, }: Props): React.ReactNode;
|
|
7
10
|
export {};
|
package/build/router/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "intentx-react-router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-z",
|
|
4
4
|
"description": "Intent-based routing for React. Navigate by intent instead of URLs.",
|
|
5
5
|
"author": "Delpi.Kye",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
|
|
8
7
|
"type": "module",
|
|
9
8
|
"main": "./build/index.js",
|
|
10
9
|
"module": "./build/index.esm.js",
|
|
@@ -16,7 +15,6 @@
|
|
|
16
15
|
"require": "./build/index.js"
|
|
17
16
|
}
|
|
18
17
|
},
|
|
19
|
-
|
|
20
18
|
"files": [
|
|
21
19
|
"build"
|
|
22
20
|
],
|
|
@@ -27,11 +25,9 @@
|
|
|
27
25
|
"watch": "rollup -c -w",
|
|
28
26
|
"typecheck": "tsc --noEmit"
|
|
29
27
|
},
|
|
30
|
-
|
|
31
28
|
"engines": {
|
|
32
29
|
"node": ">=16"
|
|
33
30
|
},
|
|
34
|
-
|
|
35
31
|
"repository": {
|
|
36
32
|
"type": "git",
|
|
37
33
|
"url": "https://github.com/delpikye-v/intentx-react-router.git"
|
|
@@ -52,7 +48,6 @@
|
|
|
52
48
|
"spa",
|
|
53
49
|
"routing"
|
|
54
50
|
],
|
|
55
|
-
|
|
56
51
|
"peerDependencies": {
|
|
57
52
|
"react": ">=18",
|
|
58
53
|
"react-dom": ">=18",
|
|
@@ -79,7 +74,6 @@
|
|
|
79
74
|
"tslib": "^2.6.2",
|
|
80
75
|
"typescript": "^5.0.0"
|
|
81
76
|
},
|
|
82
|
-
|
|
83
77
|
"publishConfig": {
|
|
84
78
|
"access": "public"
|
|
85
79
|
}
|