route-ui-sync 0.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/README.md +359 -0
- package/dist/index.cjs +38 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
# route-ui-sync
|
|
2
|
+
|
|
3
|
+
一个与 UI 框架无关的工具库,用于根据当前路由信息计算选中 key。
|
|
4
|
+
|
|
5
|
+
## 应用场景
|
|
6
|
+
|
|
7
|
+
适用于菜单选中、面包屑导航、标签页激活、侧边栏高亮等需要根据路由状态决定 UI 状态的场景,处理路由和 UI 状态不匹配的问题。
|
|
8
|
+
|
|
9
|
+
## 特性
|
|
10
|
+
|
|
11
|
+
- 🎯 **框架无关**:不依赖 React、Vue 或任何路由库
|
|
12
|
+
- 🚀 **轻量级**:零依赖,体积小
|
|
13
|
+
- 🔧 **类型安全**:完整的 TypeScript 类型定义
|
|
14
|
+
- ✅ **测试覆盖**:完整的单元测试
|
|
15
|
+
- 📦 **多格式支持**:同时支持 ES 模块和 CommonJS
|
|
16
|
+
|
|
17
|
+
## 安装
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install route-ui-sync
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 快速开始
|
|
24
|
+
|
|
25
|
+
### 3 步完成菜单自动选中
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// 1. 导入函数和类型
|
|
29
|
+
import { getSelectedKeyFromLocation, RouteSelectionRule } from 'route-ui-sync';
|
|
30
|
+
import { useLocation } from 'react-router-dom';
|
|
31
|
+
|
|
32
|
+
// 2. 定义规则:哪些路由对应哪些菜单 key
|
|
33
|
+
const rules: RouteSelectionRule[] = [
|
|
34
|
+
{ key: 'page1', match: (loc) => loc.pathname === '/page1' },
|
|
35
|
+
{ key: 'page2', match: (loc) => loc.pathname === '/page2' },
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
// 3. 获取当前路由,自动计算选中的 key
|
|
39
|
+
const location = useLocation();
|
|
40
|
+
const selectedKey = getSelectedKeyFromLocation(location, rules);
|
|
41
|
+
|
|
42
|
+
// 使用 selectedKey 来高亮菜单
|
|
43
|
+
<Menu selectedKeys={selectedKey ? [selectedKey] : []} />
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 使用方法
|
|
47
|
+
|
|
48
|
+
### 基本用法
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { getSelectedKeyFromLocation, RouteSelectionRule } from 'route-ui-sync';
|
|
52
|
+
|
|
53
|
+
// 定义路由匹配规则
|
|
54
|
+
const rules: RouteSelectionRule[] = [
|
|
55
|
+
{
|
|
56
|
+
key: 'page1',
|
|
57
|
+
match: (location) => location.pathname === '/page1',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
key: 'page2',
|
|
61
|
+
match: (location) => location.pathname === '/page2',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: 'items',
|
|
65
|
+
match: (location) => location.pathname.startsWith('/items'),
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// 根据当前 location 计算选中的 key
|
|
70
|
+
const location = { pathname: '/page1' };
|
|
71
|
+
const selectedKey = getSelectedKeyFromLocation(location, rules);
|
|
72
|
+
console.log(selectedKey); // 'page1'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 使用优先级
|
|
76
|
+
|
|
77
|
+
当多个规则同时匹配时,可以使用 `priority` 属性来控制优先级:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
const rules: RouteSelectionRule[] = [
|
|
81
|
+
{
|
|
82
|
+
key: 'item-detail',
|
|
83
|
+
match: (location) => /^\/items\/\d+$/.test(location.pathname),
|
|
84
|
+
priority: 10, // 高优先级
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
key: 'items',
|
|
88
|
+
match: (location) => location.pathname.startsWith('/items'),
|
|
89
|
+
priority: 5, // 低优先级
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
const location = { pathname: '/items/123' };
|
|
94
|
+
const selectedKey = getSelectedKeyFromLocation(location, rules);
|
|
95
|
+
console.log(selectedKey); // 'item-detail' (因为优先级更高)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 处理查询参数和哈希
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const rules: RouteSelectionRule[] = [
|
|
102
|
+
{
|
|
103
|
+
key: 'search',
|
|
104
|
+
match: (location) => {
|
|
105
|
+
return location.pathname === '/search' && location.search?.includes('q=');
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
key: 'section',
|
|
110
|
+
match: (location) => location.hash === '#section1',
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
const location = {
|
|
115
|
+
pathname: '/search',
|
|
116
|
+
search: '?q=test',
|
|
117
|
+
hash: '#section1',
|
|
118
|
+
};
|
|
119
|
+
const selectedKey = getSelectedKeyFromLocation(location, rules);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 与 React Router 集成
|
|
123
|
+
|
|
124
|
+
#### 基础示例
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { useLocation } from 'react-router-dom';
|
|
128
|
+
import { getSelectedKeyFromLocation, RouteSelectionRule } from 'route-ui-sync';
|
|
129
|
+
import { Menu } from '@arco-design/web-react';
|
|
130
|
+
|
|
131
|
+
function NavigationMenu() {
|
|
132
|
+
const location = useLocation();
|
|
133
|
+
|
|
134
|
+
// 定义菜单选中规则
|
|
135
|
+
const rules: RouteSelectionRule[] = [
|
|
136
|
+
{
|
|
137
|
+
key: 'page1',
|
|
138
|
+
match: (loc) => loc.pathname === '/page1',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
key: 'page2',
|
|
142
|
+
match: (loc) => loc.pathname === '/page2',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
key: 'items',
|
|
146
|
+
match: (loc) => loc.pathname.startsWith('/items'),
|
|
147
|
+
},
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
// 根据当前路由自动计算选中的菜单项
|
|
151
|
+
const selectedKey = getSelectedKeyFromLocation(location, rules);
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<Menu selectedKeys={selectedKey ? [selectedKey] : []}>
|
|
155
|
+
<Menu.Item key="page1">页面一</Menu.Item>
|
|
156
|
+
<Menu.Item key="page2">页面二</Menu.Item>
|
|
157
|
+
<Menu.Item key="items">项目列表</Menu.Item>
|
|
158
|
+
</Menu>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### 实际项目示例:动态菜单规则
|
|
164
|
+
|
|
165
|
+
在实际项目中,你可能需要根据动态数据(如从 API 获取的产品分类)来生成规则:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { useLocation } from 'react-router-dom';
|
|
169
|
+
import { useMemo } from 'react';
|
|
170
|
+
import { getSelectedKeyFromLocation, RouteSelectionRule } from 'route-ui-sync';
|
|
171
|
+
import { Menu } from '@arco-design/web-react';
|
|
172
|
+
|
|
173
|
+
interface Category {
|
|
174
|
+
categoryId: string;
|
|
175
|
+
name: string;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function NavigationMenu() {
|
|
179
|
+
const location = useLocation();
|
|
180
|
+
const [categories, setCategories] = useState<Category[]>([]);
|
|
181
|
+
|
|
182
|
+
// 从 API 获取分类
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
fetchCategories().then(setCategories);
|
|
185
|
+
}, []);
|
|
186
|
+
|
|
187
|
+
// 动态生成菜单选中规则
|
|
188
|
+
const menuSelectionRules: RouteSelectionRule[] = useMemo(() => {
|
|
189
|
+
const rules: RouteSelectionRule[] = [
|
|
190
|
+
{
|
|
191
|
+
key: 'page1',
|
|
192
|
+
match: (loc) => loc.pathname === '/page1',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
key: 'page2',
|
|
196
|
+
match: (loc) => loc.pathname === '/page2',
|
|
197
|
+
},
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
// 根据分类动态添加规则
|
|
201
|
+
// 例如:/item?type=category1 对应 item-1
|
|
202
|
+
categories.forEach((cat, index) => {
|
|
203
|
+
const key = `item-${index + 1}`;
|
|
204
|
+
rules.push({
|
|
205
|
+
key,
|
|
206
|
+
match: (loc) => {
|
|
207
|
+
if (loc.pathname !== '/item') return false;
|
|
208
|
+
const params = new URLSearchParams(loc.search ?? '');
|
|
209
|
+
return params.get('type') === cat.categoryId;
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return rules;
|
|
215
|
+
}, [categories]);
|
|
216
|
+
|
|
217
|
+
// 自动计算当前选中的菜单项
|
|
218
|
+
const selectedMenuKey = useMemo(
|
|
219
|
+
() => getSelectedKeyFromLocation(location, menuSelectionRules),
|
|
220
|
+
[location, menuSelectionRules]
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<Menu selectedKeys={selectedMenuKey ? [selectedMenuKey] : []}>
|
|
225
|
+
<Menu.Item key="page1">页面一</Menu.Item>
|
|
226
|
+
<Menu.Item key="page2">页面二</Menu.Item>
|
|
227
|
+
{categories.map((cat, index) => (
|
|
228
|
+
<Menu.Item key={`item-${index + 1}`}>
|
|
229
|
+
{cat.name}
|
|
230
|
+
</Menu.Item>
|
|
231
|
+
))}
|
|
232
|
+
</Menu>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### 使用优先级处理复杂场景
|
|
238
|
+
|
|
239
|
+
当路由有嵌套关系时,使用优先级确保精确匹配:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
const rules: RouteSelectionRule[] = [
|
|
243
|
+
// 精确匹配:项目详情页
|
|
244
|
+
{
|
|
245
|
+
key: 'item-detail',
|
|
246
|
+
match: (loc) => /^\/items\/\d+$/.test(loc.pathname),
|
|
247
|
+
priority: 10, // 高优先级
|
|
248
|
+
},
|
|
249
|
+
// 模糊匹配:项目列表页
|
|
250
|
+
{
|
|
251
|
+
key: 'items',
|
|
252
|
+
match: (loc) => loc.pathname.startsWith('/items'),
|
|
253
|
+
priority: 5, // 低优先级
|
|
254
|
+
},
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
// 访问 /items/123 时,会选中 'item-detail'(优先级更高)
|
|
258
|
+
// 访问 /items 时,会选中 'items'
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## API
|
|
262
|
+
|
|
263
|
+
### `getSelectedKeyFromLocation`
|
|
264
|
+
|
|
265
|
+
根据当前路由信息和一组规则,计算"当前应该选中的 key"。
|
|
266
|
+
|
|
267
|
+
#### 参数
|
|
268
|
+
|
|
269
|
+
- `location`: 包含 `pathname`、`search`(可选)、`hash`(可选)的对象
|
|
270
|
+
- `rules`: 路由选择规则数组
|
|
271
|
+
|
|
272
|
+
#### 返回值
|
|
273
|
+
|
|
274
|
+
- `string | undefined`: 匹配的 key,如果没有匹配则返回 `undefined`
|
|
275
|
+
|
|
276
|
+
### `RouteSelectionRule`
|
|
277
|
+
|
|
278
|
+
路由选择规则接口。
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
interface RouteSelectionRule {
|
|
282
|
+
/**
|
|
283
|
+
* 唯一 key,用于传给 UI 组件
|
|
284
|
+
*/
|
|
285
|
+
key: string;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* 匹配函数:根据当前 location 判断这个规则是否命中
|
|
289
|
+
*/
|
|
290
|
+
match: RouteMatchPredicate;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* 优先级:数字越大优先级越高(可选)
|
|
294
|
+
* 当多个规则同时命中时,用它来决定最终选中哪个
|
|
295
|
+
*/
|
|
296
|
+
priority?: number;
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### `RouteMatchPredicate`
|
|
301
|
+
|
|
302
|
+
匹配函数类型。
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
type RouteMatchPredicate = (location: {
|
|
306
|
+
pathname: string;
|
|
307
|
+
search?: string;
|
|
308
|
+
hash?: string;
|
|
309
|
+
}) => boolean;
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## 开发
|
|
313
|
+
|
|
314
|
+
### 构建
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
npm run build
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### 测试
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
# 运行测试
|
|
324
|
+
npm test
|
|
325
|
+
|
|
326
|
+
# 监听模式
|
|
327
|
+
npm run test:watch
|
|
328
|
+
|
|
329
|
+
# 生成覆盖率报告
|
|
330
|
+
npm run test:coverage
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### 类型检查
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
npm run type-check
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### 测试覆盖
|
|
340
|
+
|
|
341
|
+
- **测试用例数**: 39 个
|
|
342
|
+
- **代码覆盖率**: 100%
|
|
343
|
+
- **测试场景**: 包括基础功能、复杂场景、边界情况、实际项目场景等
|
|
344
|
+
|
|
345
|
+
查看 [TEST_COVERAGE.md](./TEST_COVERAGE.md) 了解详细的测试覆盖情况。
|
|
346
|
+
|
|
347
|
+
## 更多示例
|
|
348
|
+
|
|
349
|
+
查看 [USAGE_EXAMPLES.md](./USAGE_EXAMPLES.md) 获取更多详细的使用示例,包括:
|
|
350
|
+
- 基础使用场景
|
|
351
|
+
- React Router 集成
|
|
352
|
+
- 动态规则生成
|
|
353
|
+
- 优先级控制
|
|
354
|
+
- 处理查询参数
|
|
355
|
+
- 完整项目示例
|
|
356
|
+
|
|
357
|
+
## 许可证
|
|
358
|
+
|
|
359
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// 一个与 UI 框架无关的"小核心",用于根据当前路由信息计算选中 key。
|
|
4
|
+
/**
|
|
5
|
+
* 根据当前路由信息和一组规则,计算"当前应该选中的 key"
|
|
6
|
+
*
|
|
7
|
+
* - 不依赖 React / react-router / 任何 UI 库
|
|
8
|
+
* - 只关心最基础的 location 对象和规则数组
|
|
9
|
+
* - 未来做成 npm 包时,只需要把这个函数原样搬过去即可
|
|
10
|
+
*/
|
|
11
|
+
function getSelectedKeyFromLocation(location, rules) {
|
|
12
|
+
if (!location || !rules || rules.length === 0) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
const { pathname, search = "", hash = "" } = location;
|
|
16
|
+
const safeLocation = { pathname, search, hash };
|
|
17
|
+
// 找出所有命中的规则
|
|
18
|
+
const matched = rules.filter((rule) => {
|
|
19
|
+
try {
|
|
20
|
+
return rule.match(safeLocation);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// 某个规则内部异常时,忽略该规则,避免影响整体
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
if (matched.length === 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
// 如果只有一个命中,直接返回
|
|
30
|
+
if (matched.length === 1)
|
|
31
|
+
return matched[0].key;
|
|
32
|
+
// 多个命中时,按 priority 从高到低排序,取第一个
|
|
33
|
+
matched.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
34
|
+
return matched[0].key;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
exports.getSelectedKeyFromLocation = getSelectedKeyFromLocation;
|
|
38
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAAA;AAwBA;;;;;;AAMG;AACG,SAAU,0BAA0B,CACxC,QAA8D,EAC9D,KAA2B,EAAA;AAE3B,IAAA,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;AAC7C,QAAA,OAAO,SAAS;IAClB;AAEA,IAAA,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,QAAQ;IACrD,MAAM,YAAY,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE;;IAG/C,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,KAAI;AACpC,QAAA,IAAI;AACF,YAAA,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC;QACjC;AAAE,QAAA,MAAM;;AAEN,YAAA,OAAO,KAAK;QACd;AACF,IAAA,CAAC,CAAC;AAEF,IAAA,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,SAAS;;AAG1C,IAAA,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG;;IAG/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;AAC7D,IAAA,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG;AACvB;;;;"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
type RouteMatchPredicate = (location: {
|
|
2
|
+
pathname: string;
|
|
3
|
+
search?: string;
|
|
4
|
+
hash?: string;
|
|
5
|
+
}) => boolean;
|
|
6
|
+
interface RouteSelectionRule {
|
|
7
|
+
/**
|
|
8
|
+
* 唯一 key,用于传给 UI 组件(例如 Menu.Item 的 key 或 className 中的一部分)
|
|
9
|
+
*/
|
|
10
|
+
key: string;
|
|
11
|
+
/**
|
|
12
|
+
* 匹配函数:根据当前 location 判断这个规则是否命中
|
|
13
|
+
*/
|
|
14
|
+
match: RouteMatchPredicate;
|
|
15
|
+
/**
|
|
16
|
+
* 优先级:数字越大优先级越高(可选)
|
|
17
|
+
* 当多个规则同时命中时,用它来决定最终选中哪个
|
|
18
|
+
*/
|
|
19
|
+
priority?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 根据当前路由信息和一组规则,计算"当前应该选中的 key"
|
|
23
|
+
*
|
|
24
|
+
* - 不依赖 React / react-router / 任何 UI 库
|
|
25
|
+
* - 只关心最基础的 location 对象和规则数组
|
|
26
|
+
* - 未来做成 npm 包时,只需要把这个函数原样搬过去即可
|
|
27
|
+
*/
|
|
28
|
+
declare function getSelectedKeyFromLocation(location: {
|
|
29
|
+
pathname: string;
|
|
30
|
+
search?: string;
|
|
31
|
+
hash?: string;
|
|
32
|
+
}, rules: RouteSelectionRule[]): string | undefined;
|
|
33
|
+
|
|
34
|
+
export { getSelectedKeyFromLocation };
|
|
35
|
+
export type { RouteMatchPredicate, RouteSelectionRule };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// 一个与 UI 框架无关的"小核心",用于根据当前路由信息计算选中 key。
|
|
2
|
+
/**
|
|
3
|
+
* 根据当前路由信息和一组规则,计算"当前应该选中的 key"
|
|
4
|
+
*
|
|
5
|
+
* - 不依赖 React / react-router / 任何 UI 库
|
|
6
|
+
* - 只关心最基础的 location 对象和规则数组
|
|
7
|
+
* - 未来做成 npm 包时,只需要把这个函数原样搬过去即可
|
|
8
|
+
*/
|
|
9
|
+
function getSelectedKeyFromLocation(location, rules) {
|
|
10
|
+
if (!location || !rules || rules.length === 0) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const { pathname, search = "", hash = "" } = location;
|
|
14
|
+
const safeLocation = { pathname, search, hash };
|
|
15
|
+
// 找出所有命中的规则
|
|
16
|
+
const matched = rules.filter((rule) => {
|
|
17
|
+
try {
|
|
18
|
+
return rule.match(safeLocation);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// 某个规则内部异常时,忽略该规则,避免影响整体
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
if (matched.length === 0)
|
|
26
|
+
return undefined;
|
|
27
|
+
// 如果只有一个命中,直接返回
|
|
28
|
+
if (matched.length === 1)
|
|
29
|
+
return matched[0].key;
|
|
30
|
+
// 多个命中时,按 priority 从高到低排序,取第一个
|
|
31
|
+
matched.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
32
|
+
return matched[0].key;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { getSelectedKeyFromLocation };
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":[null],"names":[],"mappings":"AAAA;AAwBA;;;;;;AAMG;AACG,SAAU,0BAA0B,CACxC,QAA8D,EAC9D,KAA2B,EAAA;AAE3B,IAAA,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;AAC7C,QAAA,OAAO,SAAS;IAClB;AAEA,IAAA,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,QAAQ;IACrD,MAAM,YAAY,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE;;IAG/C,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,KAAI;AACpC,QAAA,IAAI;AACF,YAAA,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC;QACjC;AAAE,QAAA,MAAM;;AAEN,YAAA,OAAO,KAAK;QACd;AACF,IAAA,CAAC,CAAC;AAEF,IAAA,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,SAAS;;AAG1C,IAAA,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG;;IAG/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;AAC7D,IAAA,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG;AACvB;;;;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "route-ui-sync",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A lightweight, framework-agnostic utility for automatically matching routes to menu keys. Zero dependencies, TypeScript support, for navigation menus.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "rollup -c",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"test:coverage": "vitest run --coverage",
|
|
25
|
+
"type-check": "tsc --noEmit",
|
|
26
|
+
"prepublishOnly": "npm run build && npm run test"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"route",
|
|
30
|
+
"routing",
|
|
31
|
+
"navigation",
|
|
32
|
+
"location",
|
|
33
|
+
"selection",
|
|
34
|
+
"framework-agnostic"
|
|
35
|
+
],
|
|
36
|
+
"author": "",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@rollup/plugin-typescript": "^12.1.1",
|
|
40
|
+
"@types/node": "^22.7.4",
|
|
41
|
+
"@vitest/coverage-v8": "^4.0.15",
|
|
42
|
+
"rollup": "^4.22.0",
|
|
43
|
+
"rollup-plugin-dts": "^6.1.1",
|
|
44
|
+
"tslib": "^2.8.1",
|
|
45
|
+
"typescript": "^5.5.3",
|
|
46
|
+
"vitest": "^4.0.15"
|
|
47
|
+
}
|
|
48
|
+
}
|