@navojs/react-router 1.0.1
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 +148 -0
- package/dist/index.js +134 -0
- package/package.json +50 -0
- package/src/hooks.ts +80 -0
- package/src/index.ts +4 -0
- package/src/main.tsx +106 -0
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# @navojs/react-router
|
|
2
|
+
|
|
3
|
+
React Router integration for [Navo](https://github.com/flycran/navo) — declarative navigation and permission-based routing.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @navojs/react-router react-router
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### 1. Define your navigation structure
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { createNavo } from '@navojs/react-router'
|
|
17
|
+
|
|
18
|
+
const navo = createNavo([
|
|
19
|
+
{ id: 'dashboard', label: 'Dashboard', path: '/dashboard' },
|
|
20
|
+
{
|
|
21
|
+
id: 'users',
|
|
22
|
+
label: 'Users',
|
|
23
|
+
path: '/users',
|
|
24
|
+
children: [
|
|
25
|
+
{ id: 'user-list', label: 'User List', path: 'list' },
|
|
26
|
+
{ id: 'user-detail', label: 'User Detail', path: ':id', affiliated: true },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
{ id: 'settings', label: 'Settings', path: '/settings' },
|
|
30
|
+
])
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Wrap your app with `NavoProvider`
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { NavoProvider } from '@navojs/react-router'
|
|
37
|
+
import { BrowserRouter } from 'react-router'
|
|
38
|
+
|
|
39
|
+
function App() {
|
|
40
|
+
return (
|
|
41
|
+
<BrowserRouter>
|
|
42
|
+
<NavoProvider navo={navo}>
|
|
43
|
+
<AppRoutes />
|
|
44
|
+
</NavoProvider>
|
|
45
|
+
</BrowserRouter>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 3. Generate routes from your navo structure
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { generateRoutes } from '@navojs/react-router'
|
|
54
|
+
import { useRoutes } from 'react-router'
|
|
55
|
+
|
|
56
|
+
function AppRoutes() {
|
|
57
|
+
const routes = generateRoutes(navo)
|
|
58
|
+
return useRoutes(routes)
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Permission Control
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { useCanAccess } from '@navojs/react-router'
|
|
66
|
+
|
|
67
|
+
function PermissionGate() {
|
|
68
|
+
const onCanAccess = useCanAccess()
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
// Provide your access control logic
|
|
72
|
+
onCanAccess((node) => {
|
|
73
|
+
return userPermissions.includes(node.id)
|
|
74
|
+
})
|
|
75
|
+
}, [])
|
|
76
|
+
|
|
77
|
+
return null
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Hooks
|
|
82
|
+
|
|
83
|
+
### `useCanAccess()`
|
|
84
|
+
|
|
85
|
+
Returns the `onCanAccess` callback for setting up permission checks.
|
|
86
|
+
|
|
87
|
+
### `useMatchedNodes()`
|
|
88
|
+
|
|
89
|
+
Returns the currently active navigation nodes and their IDs — useful for highlighting menus and breadcrumbs.
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
const { matchedNodes, matchedIds } = useMatchedNodes()
|
|
93
|
+
// matchedIds: ['dashboard', 'users', 'user-list']
|
|
94
|
+
// matchedNodes: corresponding NavoNode objects
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### `useNavo()`
|
|
98
|
+
|
|
99
|
+
Returns utility functions for working with the navigation tree:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
const { nodes, getNodeById, getPathById, hasCanAccess } = useNavo()
|
|
103
|
+
|
|
104
|
+
// Get all authorized nodes
|
|
105
|
+
nodes.forEach(node => console.log(node.label))
|
|
106
|
+
|
|
107
|
+
// Check access
|
|
108
|
+
if (hasCanAccess('settings')) { /* ... */ }
|
|
109
|
+
|
|
110
|
+
// Get path by id
|
|
111
|
+
const path = getPathById('user-list') // '/users/list'
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Route Escape Hatch
|
|
115
|
+
|
|
116
|
+
You can pass native React Router route properties through the `route` field:
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
createNavo([
|
|
120
|
+
{
|
|
121
|
+
id: 'home',
|
|
122
|
+
path: '/',
|
|
123
|
+
route: {
|
|
124
|
+
loader: () => fetchData(),
|
|
125
|
+
caseSensitive: true,
|
|
126
|
+
errorElement: <ErrorPage />,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
])
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## API
|
|
133
|
+
|
|
134
|
+
### `generateRoutes(navo)`
|
|
135
|
+
|
|
136
|
+
Converts a Navo instance into an array of React Router `RouteObject`s with automatic index redirects.
|
|
137
|
+
|
|
138
|
+
### `NavoProvider`
|
|
139
|
+
|
|
140
|
+
React context provider that holds the Navo instance and authentication state.
|
|
141
|
+
|
|
142
|
+
### `NavoRedirect`
|
|
143
|
+
|
|
144
|
+
Internal component that handles automatic redirection to the resolved path (e.g., redirecting `/users` to `/users/list`).
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
export * from "@navojs/core";
|
|
3
|
+
|
|
4
|
+
// src/hooks.ts
|
|
5
|
+
import { Navo } from "@navojs/core";
|
|
6
|
+
import { useContext as useContext2, useMemo as useMemo2 } from "react";
|
|
7
|
+
import { useMatches } from "react-router";
|
|
8
|
+
|
|
9
|
+
// src/main.tsx
|
|
10
|
+
import { createContext, useContext, useMemo, useState } from "react";
|
|
11
|
+
import {
|
|
12
|
+
Navigate,
|
|
13
|
+
useLocation
|
|
14
|
+
} from "react-router";
|
|
15
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
16
|
+
function NavoRedirect({ navNode }) {
|
|
17
|
+
const navo = useContext(NavoContext);
|
|
18
|
+
const location = useLocation();
|
|
19
|
+
const resolveNode = navNode ? navo?.authedIdMap.get(navNode.id) : null;
|
|
20
|
+
if (resolveNode)
|
|
21
|
+
return /* @__PURE__ */ jsxDEV(Navigate, {
|
|
22
|
+
to: resolveNode.path,
|
|
23
|
+
replace: true
|
|
24
|
+
}, undefined, false, undefined, this);
|
|
25
|
+
if (navo && location.pathname === navo.originNavo.basePath)
|
|
26
|
+
return /* @__PURE__ */ jsxDEV(Navigate, {
|
|
27
|
+
to: navo.authedRootResolvePath,
|
|
28
|
+
replace: true
|
|
29
|
+
}, undefined, false, undefined, this);
|
|
30
|
+
}
|
|
31
|
+
var NavoContext = createContext(null);
|
|
32
|
+
function NavoProvider({ children, navo }) {
|
|
33
|
+
const [state, setState] = useState({
|
|
34
|
+
nodes: navo.nodes,
|
|
35
|
+
idMap: navo.idMap,
|
|
36
|
+
authedNodes: navo.authedNodes,
|
|
37
|
+
authedIdMap: navo.authedIdMap,
|
|
38
|
+
authedRootResolvePath: navo.authedRootResolvePath
|
|
39
|
+
});
|
|
40
|
+
const value = useMemo(() => ({
|
|
41
|
+
...state,
|
|
42
|
+
originNavo: navo,
|
|
43
|
+
onCanAccess: (canAccess) => {
|
|
44
|
+
navo.authenticat(canAccess);
|
|
45
|
+
setState({
|
|
46
|
+
nodes: navo.nodes,
|
|
47
|
+
idMap: navo.idMap,
|
|
48
|
+
authedNodes: navo.authedNodes,
|
|
49
|
+
authedIdMap: navo.authedIdMap,
|
|
50
|
+
authedRootResolvePath: navo.authedRootResolvePath
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}), [navo, state]);
|
|
54
|
+
return /* @__PURE__ */ jsxDEV(NavoContext.Provider, {
|
|
55
|
+
value,
|
|
56
|
+
children
|
|
57
|
+
}, undefined, false, undefined, this);
|
|
58
|
+
}
|
|
59
|
+
function generateRoutes(navo) {
|
|
60
|
+
const navNodes = navo.nodes;
|
|
61
|
+
const r = (navNodes2) => {
|
|
62
|
+
const routes = [];
|
|
63
|
+
for (let i = 0;i < navNodes2.length; i++) {
|
|
64
|
+
const navNode = navNodes2[i];
|
|
65
|
+
const route = {
|
|
66
|
+
...navNode.route,
|
|
67
|
+
id: navNode.id,
|
|
68
|
+
path: navNode.originPath
|
|
69
|
+
};
|
|
70
|
+
if (navNode.children?.length) {
|
|
71
|
+
route.children = r(navNode.children);
|
|
72
|
+
}
|
|
73
|
+
routes.push(route);
|
|
74
|
+
}
|
|
75
|
+
if (navNodes2.length) {
|
|
76
|
+
routes.push({
|
|
77
|
+
index: true,
|
|
78
|
+
element: /* @__PURE__ */ jsxDEV(NavoRedirect, {
|
|
79
|
+
navNode: navNodes2[0].parent
|
|
80
|
+
}, undefined, false, undefined, this)
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return routes;
|
|
84
|
+
};
|
|
85
|
+
return r(navNodes);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/hooks.ts
|
|
89
|
+
var useCanAccess = () => {
|
|
90
|
+
const navo = useContext2(NavoContext);
|
|
91
|
+
if (!navo)
|
|
92
|
+
throw new Error("useCanAccess must be used within a NavoProvider.");
|
|
93
|
+
return navo.onCanAccess;
|
|
94
|
+
};
|
|
95
|
+
var useMatchedNodes = () => {
|
|
96
|
+
const navo = useContext2(NavoContext);
|
|
97
|
+
if (!navo)
|
|
98
|
+
throw new Error("useCurrentNode must be used within a NavoProvider.");
|
|
99
|
+
const matches = useMatches();
|
|
100
|
+
return useMemo2(() => {
|
|
101
|
+
const matchedNodes = [];
|
|
102
|
+
const matchedIds = [];
|
|
103
|
+
for (let i = matches.length - 1;i >= 0; i--) {
|
|
104
|
+
const element = matches[i];
|
|
105
|
+
const node = navo.authedIdMap.get(element.id);
|
|
106
|
+
if (node && !Navo.isAffiliated(node)) {
|
|
107
|
+
matchedNodes.push(node);
|
|
108
|
+
matchedIds.push(node.id);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
matchedNodes: matchedNodes.reverse(),
|
|
113
|
+
matchedIds: matchedIds.reverse()
|
|
114
|
+
};
|
|
115
|
+
}, [matches, navo.authedIdMap]);
|
|
116
|
+
};
|
|
117
|
+
var useNavo = () => {
|
|
118
|
+
const navo = useContext2(NavoContext);
|
|
119
|
+
if (!navo)
|
|
120
|
+
throw new Error("useNavoIdMap must be used within a NavoProvider.");
|
|
121
|
+
return {
|
|
122
|
+
nodes: navo.authedNodes,
|
|
123
|
+
getNodeById: (id) => navo.authedIdMap.get(id),
|
|
124
|
+
getPathById: (id) => navo.authedIdMap.get(id)?.path,
|
|
125
|
+
hasCanAccess: (id) => navo.authedIdMap.has(id)
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
export {
|
|
129
|
+
useNavo,
|
|
130
|
+
useMatchedNodes,
|
|
131
|
+
useCanAccess,
|
|
132
|
+
generateRoutes,
|
|
133
|
+
NavoProvider
|
|
134
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@navojs/react-router",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "React Router integration for Navo — declarative navigation and permission-based routing",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "bun --watch ./build.ts",
|
|
8
|
+
"build": "bun ./build.ts",
|
|
9
|
+
"check": "tsgo --noEmit --project tsconfig.json",
|
|
10
|
+
"test": "tsgo --noEmit --project tsconfig.test.json && bun test"
|
|
11
|
+
},
|
|
12
|
+
"main": "dist/index.js",
|
|
13
|
+
"module": "dist/index.js",
|
|
14
|
+
"types": "src/index.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"require": "./dist/index.js",
|
|
18
|
+
"types": "./src/index.ts"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"navo",
|
|
26
|
+
"react-router",
|
|
27
|
+
"navigation",
|
|
28
|
+
"routing",
|
|
29
|
+
"permissions",
|
|
30
|
+
"acl"
|
|
31
|
+
],
|
|
32
|
+
"author": "flycran",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/flycran/navo",
|
|
37
|
+
"directory": "packages/react-router"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@navojs/core": "workspace:*",
|
|
41
|
+
"@types/react": "19.2.14"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@navojs/config": "workspace:*"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"react-router": "^7.18.0",
|
|
48
|
+
"react": ">=18"
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/hooks.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Navo, type NavoNode } from '@navojs/core'
|
|
2
|
+
import { useContext, useMemo } from 'react'
|
|
3
|
+
import { useMatches } from 'react-router'
|
|
4
|
+
import { NavoContext } from './main'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 鉴权回调
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const onCanAccess = useCanAccess()
|
|
11
|
+
* useEffect(() => {
|
|
12
|
+
* onCanAccess((node) => {
|
|
13
|
+
* return accessList.includes(node.id)
|
|
14
|
+
* })
|
|
15
|
+
* }, [])
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export const useCanAccess = () => {
|
|
19
|
+
const navo = useContext(NavoContext)
|
|
20
|
+
if (!navo) throw new Error('useCanAccess must be used within a NavoProvider.')
|
|
21
|
+
return navo.onCanAccess
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 返回当前活动的节点匹配项
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const { matchedIds } = useMatchedNodes()
|
|
29
|
+
* const openKeys = matchedIds.slice(0, -1)
|
|
30
|
+
* const selectedKey = matchedIds.at(-1)
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export const useMatchedNodes = () => {
|
|
34
|
+
const navo = useContext(NavoContext)
|
|
35
|
+
if (!navo) throw new Error('useCurrentNode must be used within a NavoProvider.')
|
|
36
|
+
const matches = useMatches()
|
|
37
|
+
|
|
38
|
+
return useMemo(() => {
|
|
39
|
+
const matchedNodes: NavoNode[] = []
|
|
40
|
+
const matchedIds: string[] = []
|
|
41
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
42
|
+
const element = matches[i]
|
|
43
|
+
const node = navo.authedIdMap.get(element.id)
|
|
44
|
+
|
|
45
|
+
if (node && !Navo.isAffiliated(node)) {
|
|
46
|
+
matchedNodes.push(node)
|
|
47
|
+
matchedIds.push(node.id)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
matchedNodes: matchedNodes.reverse(),
|
|
53
|
+
matchedIds: matchedIds.reverse(),
|
|
54
|
+
}
|
|
55
|
+
}, [matches, navo.authedIdMap])
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type UseNavoReturn = {
|
|
59
|
+
/** 已鉴权节点 */
|
|
60
|
+
nodes: NavoNode[]
|
|
61
|
+
/** 根据id获取节点 */
|
|
62
|
+
getNodeById: (id: string) => NavoNode | undefined
|
|
63
|
+
/** 根据id获取路径 */
|
|
64
|
+
getPathById: (id: string) => string | undefined
|
|
65
|
+
/** 判断是否有权限访问该节点 */
|
|
66
|
+
hasCanAccess: (id: string) => boolean
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** 获取Navo节点和实用工具 */
|
|
70
|
+
export const useNavo = (): UseNavoReturn => {
|
|
71
|
+
const navo = useContext(NavoContext)
|
|
72
|
+
if (!navo) throw new Error('useNavoIdMap must be used within a NavoProvider.')
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
nodes: navo.authedNodes,
|
|
76
|
+
getNodeById: (id: string) => navo.authedIdMap.get(id),
|
|
77
|
+
getPathById: (id: string) => navo.authedIdMap.get(id)?.path,
|
|
78
|
+
hasCanAccess: (id: string) => navo.authedIdMap.has(id),
|
|
79
|
+
}
|
|
80
|
+
}
|
package/src/index.ts
ADDED
package/src/main.tsx
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { CanAccess, Navo, NavoNode, NavoNodeInput } from '@navojs/core'
|
|
2
|
+
import { createContext, useContext, useMemo, useState } from 'react'
|
|
3
|
+
import {
|
|
4
|
+
type BaseRouteObject,
|
|
5
|
+
Navigate,
|
|
6
|
+
type NonIndexRouteObject,
|
|
7
|
+
type RouteObject,
|
|
8
|
+
useLocation,
|
|
9
|
+
} from 'react-router'
|
|
10
|
+
|
|
11
|
+
export type NavNodeEscapeHatch = Omit<BaseRouteObject, 'id' | 'path'>
|
|
12
|
+
|
|
13
|
+
declare module '@navojs/core' {
|
|
14
|
+
interface NavoNodeInput {
|
|
15
|
+
route?: NavNodeEscapeHatch
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** 内置的自动重定向到真实页面 */
|
|
20
|
+
function NavoRedirect({ navNode }: { navNode?: NavoNode | null }) {
|
|
21
|
+
const navo = useContext(NavoContext)
|
|
22
|
+
const location = useLocation()
|
|
23
|
+
|
|
24
|
+
const resolveNode = navNode ? navo?.authedIdMap.get(navNode.id) : null
|
|
25
|
+
if (resolveNode) return <Navigate to={resolveNode.path} replace />
|
|
26
|
+
if (navo && location.pathname === navo.originNavo.basePath)
|
|
27
|
+
return <Navigate to={navo.authedRootResolvePath} replace />
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface NavoContextProps {
|
|
31
|
+
originNavo: Navo<NavoNodeInput[]>
|
|
32
|
+
nodes: NavoNode[]
|
|
33
|
+
idMap: Map<string, NavoNode>
|
|
34
|
+
authedNodes: NavoNode[]
|
|
35
|
+
authedIdMap: Map<string, NavoNode>
|
|
36
|
+
authedRootResolvePath: string
|
|
37
|
+
onCanAccess: (node: CanAccess) => void
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const NavoContext = createContext<NavoContextProps | null>(null)
|
|
41
|
+
|
|
42
|
+
export type NavoProviderProps<T extends NavoNodeInput[]> = {
|
|
43
|
+
navo: Navo<T>
|
|
44
|
+
children?: React.ReactNode
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Navo Provider */
|
|
48
|
+
export function NavoProvider<T extends NavoNodeInput[]>({ children, navo }: NavoProviderProps<T>) {
|
|
49
|
+
const [state, setState] = useState({
|
|
50
|
+
nodes: navo.nodes,
|
|
51
|
+
idMap: navo.idMap,
|
|
52
|
+
authedNodes: navo.authedNodes,
|
|
53
|
+
authedIdMap: navo.authedIdMap,
|
|
54
|
+
authedRootResolvePath: navo.authedRootResolvePath,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const value = useMemo(
|
|
58
|
+
() => ({
|
|
59
|
+
...state,
|
|
60
|
+
originNavo: navo,
|
|
61
|
+
onCanAccess: (canAccess?: CanAccess) => {
|
|
62
|
+
navo.authenticat(canAccess)
|
|
63
|
+
setState({
|
|
64
|
+
nodes: navo.nodes,
|
|
65
|
+
idMap: navo.idMap,
|
|
66
|
+
authedNodes: navo.authedNodes,
|
|
67
|
+
authedIdMap: navo.authedIdMap,
|
|
68
|
+
authedRootResolvePath: navo.authedRootResolvePath,
|
|
69
|
+
})
|
|
70
|
+
},
|
|
71
|
+
}),
|
|
72
|
+
[navo, state]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
return <NavoContext.Provider value={value}>{children}</NavoContext.Provider>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 生成react router routes */
|
|
79
|
+
export function generateRoutes<T extends NavoNodeInput[]>(navo: Navo<T>): RouteObject[] {
|
|
80
|
+
const navNodes = navo.nodes
|
|
81
|
+
const r = (navNodes: NavoNode[]) => {
|
|
82
|
+
const routes: RouteObject[] = []
|
|
83
|
+
for (let i = 0; i < navNodes.length; i++) {
|
|
84
|
+
const navNode = navNodes[i]
|
|
85
|
+
const route: NonIndexRouteObject = {
|
|
86
|
+
...navNode.route,
|
|
87
|
+
id: navNode.id,
|
|
88
|
+
path: navNode.originPath,
|
|
89
|
+
}
|
|
90
|
+
if (navNode.children?.length) {
|
|
91
|
+
route.children = r(navNode.children)
|
|
92
|
+
}
|
|
93
|
+
routes.push(route)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (navNodes.length) {
|
|
97
|
+
routes.push({
|
|
98
|
+
index: true,
|
|
99
|
+
element: <NavoRedirect navNode={navNodes[0].parent} />,
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
return routes
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return r(navNodes)
|
|
106
|
+
}
|