@real-router/vue 0.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 +430 -0
- package/dist/cjs/index.d.ts +244 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/metafile-cjs.json +1 -0
- package/dist/esm/index.d.mts +244 -0
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/metafile-esm.json +1 -0
- package/package.json +76 -0
- package/src/RouterProvider.ts +69 -0
- package/src/components/Link.ts +97 -0
- package/src/components/RouteView/RouteView.ts +152 -0
- package/src/components/RouteView/components.ts +31 -0
- package/src/components/RouteView/helpers.ts +110 -0
- package/src/components/RouteView/index.ts +7 -0
- package/src/components/RouteView/types.ts +14 -0
- package/src/composables/useIsActiveRoute.ts +23 -0
- package/src/composables/useNavigator.ts +15 -0
- package/src/composables/useRoute.ts +15 -0
- package/src/composables/useRouteNode.ts +38 -0
- package/src/composables/useRouteUtils.ts +12 -0
- package/src/composables/useRouter.ts +15 -0
- package/src/composables/useRouterTransition.ts +15 -0
- package/src/constants.ts +9 -0
- package/src/context.ts +9 -0
- package/src/createRouterPlugin.ts +37 -0
- package/src/directives/vLink.ts +116 -0
- package/src/index.ts +43 -0
- package/src/types.ts +24 -0
- package/src/useRefFromSource.ts +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
# @real-router/vue
|
|
2
|
+
|
|
3
|
+
[](../../LICENSE)
|
|
4
|
+
|
|
5
|
+
> Vue 3 integration for [Real-Router](https://github.com/greydragon888/real-router) — composables, components, and context providers.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @real-router/vue @real-router/core @real-router/browser-plugin
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Peer dependency:** `vue` >= 3.3.0
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createRouter } from "@real-router/core";
|
|
19
|
+
import { browserPluginFactory } from "@real-router/browser-plugin";
|
|
20
|
+
import { RouterProvider, RouteView, Link } from "@real-router/vue";
|
|
21
|
+
import { defineComponent, h } from "vue";
|
|
22
|
+
|
|
23
|
+
const router = createRouter([
|
|
24
|
+
{ name: "home", path: "/" },
|
|
25
|
+
{
|
|
26
|
+
name: "users",
|
|
27
|
+
path: "/users",
|
|
28
|
+
children: [{ name: "profile", path: "/:id" }],
|
|
29
|
+
},
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
router.usePlugin(browserPluginFactory());
|
|
33
|
+
router.start();
|
|
34
|
+
|
|
35
|
+
const App = defineComponent({
|
|
36
|
+
setup() {
|
|
37
|
+
return () =>
|
|
38
|
+
h(
|
|
39
|
+
RouterProvider,
|
|
40
|
+
{ router },
|
|
41
|
+
{
|
|
42
|
+
default: () => [
|
|
43
|
+
h("nav", [
|
|
44
|
+
h(Link, { routeName: "home" }, { default: () => "Home" }),
|
|
45
|
+
h(Link, { routeName: "users" }, { default: () => "Users" }),
|
|
46
|
+
]),
|
|
47
|
+
h(
|
|
48
|
+
RouteView,
|
|
49
|
+
{ nodeName: "" },
|
|
50
|
+
{
|
|
51
|
+
default: () => [
|
|
52
|
+
h(
|
|
53
|
+
RouteView.Match,
|
|
54
|
+
{ segment: "home" },
|
|
55
|
+
{ default: () => h(HomePage) },
|
|
56
|
+
),
|
|
57
|
+
h(
|
|
58
|
+
RouteView.Match,
|
|
59
|
+
{ segment: "users" },
|
|
60
|
+
{ default: () => h(UsersPage) },
|
|
61
|
+
),
|
|
62
|
+
h(RouteView.NotFound, null, {
|
|
63
|
+
default: () => h(NotFoundPage),
|
|
64
|
+
}),
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
),
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Or with Vue SFC templates (the composables and components work in `.vue` files too):
|
|
76
|
+
|
|
77
|
+
```vue
|
|
78
|
+
<template>
|
|
79
|
+
<RouterProvider :router="router">
|
|
80
|
+
<nav>
|
|
81
|
+
<Link routeName="home">Home</Link>
|
|
82
|
+
<Link routeName="users">Users</Link>
|
|
83
|
+
</nav>
|
|
84
|
+
<RouteView nodeName="">
|
|
85
|
+
<RouteView.Match segment="home">
|
|
86
|
+
<HomePage />
|
|
87
|
+
</RouteView.Match>
|
|
88
|
+
<RouteView.Match segment="users">
|
|
89
|
+
<UsersPage />
|
|
90
|
+
</RouteView.Match>
|
|
91
|
+
<RouteView.NotFound>
|
|
92
|
+
<NotFoundPage />
|
|
93
|
+
</RouteView.NotFound>
|
|
94
|
+
</RouteView>
|
|
95
|
+
</RouterProvider>
|
|
96
|
+
</template>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Plugin Installation (Alternative)
|
|
100
|
+
|
|
101
|
+
Use `createRouterPlugin` for the standard `app.use()` pattern instead of `<RouterProvider>`:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { createApp } from "vue";
|
|
105
|
+
import { createRouterPlugin } from "@real-router/vue";
|
|
106
|
+
|
|
107
|
+
const app = createApp(App);
|
|
108
|
+
app.use(createRouterPlugin(router));
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
All composables (`useRouter`, `useRoute`, etc.) and the `v-link` directive work identically — `app.provide()` resolves the same way as component-level `provide()`. `<RouterProvider>` remains available for advanced cases (multiple routers, scoped routing, testing).
|
|
112
|
+
|
|
113
|
+
## Composables
|
|
114
|
+
|
|
115
|
+
Route state composables return `ShallowRef` values. Read `.value` in script, or use them directly in templates where Vue auto-unwraps refs.
|
|
116
|
+
|
|
117
|
+
| Composable | Returns | Reactive? |
|
|
118
|
+
| ----------------------- | ------------------------------------------------------------- | ---------------------------------------- |
|
|
119
|
+
| `useRouter()` | `Router` | Never |
|
|
120
|
+
| `useNavigator()` | `Navigator` | Never (stable ref, safe to use directly) |
|
|
121
|
+
| `useRoute()` | `{ navigator, route: ShallowRef, previousRoute: ShallowRef }` | route/previousRoute on every navigation |
|
|
122
|
+
| `useRouteNode(name)` | `{ navigator, route: ShallowRef, previousRoute: ShallowRef }` | Only when node activates/deactivates |
|
|
123
|
+
| `useRouteUtils()` | `RouteUtils` | Never |
|
|
124
|
+
| `useRouterTransition()` | `ShallowRef<RouterTransitionSnapshot>` | On transition start/end |
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// useRouteNode — updates only when "users.*" changes
|
|
128
|
+
const UsersLayout = defineComponent({
|
|
129
|
+
setup() {
|
|
130
|
+
const { route } = useRouteNode("users");
|
|
131
|
+
|
|
132
|
+
return () => {
|
|
133
|
+
if (!route.value) return null;
|
|
134
|
+
|
|
135
|
+
switch (route.value.name) {
|
|
136
|
+
case "users":
|
|
137
|
+
return h(UsersList);
|
|
138
|
+
case "users.profile":
|
|
139
|
+
return h(UserProfile, { id: route.value.params.id });
|
|
140
|
+
default:
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// useNavigator — stable reference, never reactive
|
|
148
|
+
const BackButton = defineComponent({
|
|
149
|
+
setup() {
|
|
150
|
+
const navigator = useNavigator();
|
|
151
|
+
return () =>
|
|
152
|
+
h("button", { onClick: () => navigator.navigate("home") }, "Back");
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// useRouterTransition — progress bars, loading states
|
|
157
|
+
const GlobalProgress = defineComponent({
|
|
158
|
+
setup() {
|
|
159
|
+
const transition = useRouterTransition();
|
|
160
|
+
return () =>
|
|
161
|
+
transition.value.isTransitioning
|
|
162
|
+
? h("div", { class: "progress-bar" })
|
|
163
|
+
: null;
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Components
|
|
169
|
+
|
|
170
|
+
### `<Link>`
|
|
171
|
+
|
|
172
|
+
Navigation link with automatic active state detection. Uses `computed()` for href and class — only the DOM attributes update when active state changes.
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
h(
|
|
176
|
+
Link,
|
|
177
|
+
{
|
|
178
|
+
routeName: "users.profile",
|
|
179
|
+
routeParams: { id: "123" },
|
|
180
|
+
activeClassName: "active", // default: "active"
|
|
181
|
+
activeStrict: false, // default: false (ancestor match)
|
|
182
|
+
ignoreQueryParams: true, // default: true
|
|
183
|
+
routeOptions: { replace: true },
|
|
184
|
+
},
|
|
185
|
+
{ default: () => "View Profile" },
|
|
186
|
+
);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
In a template:
|
|
190
|
+
|
|
191
|
+
```vue
|
|
192
|
+
<Link
|
|
193
|
+
routeName="users.profile"
|
|
194
|
+
:routeParams="{ id: '123' }"
|
|
195
|
+
activeClassName="active"
|
|
196
|
+
:activeStrict="false"
|
|
197
|
+
:ignoreQueryParams="true"
|
|
198
|
+
:routeOptions="{ replace: true }"
|
|
199
|
+
>
|
|
200
|
+
View Profile
|
|
201
|
+
</Link>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### `<RouteView>`
|
|
205
|
+
|
|
206
|
+
Declarative route matching. Renders the first matching `<RouteView.Match>` child.
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
h(
|
|
210
|
+
RouteView,
|
|
211
|
+
{ nodeName: "" },
|
|
212
|
+
{
|
|
213
|
+
default: () => [
|
|
214
|
+
h(RouteView.Match, { segment: "users" }, { default: () => h(UsersPage) }),
|
|
215
|
+
h(
|
|
216
|
+
RouteView.Match,
|
|
217
|
+
{ segment: "settings" },
|
|
218
|
+
{ default: () => h(SettingsPage) },
|
|
219
|
+
),
|
|
220
|
+
h(RouteView.NotFound, null, { default: () => h(NotFoundPage) }),
|
|
221
|
+
],
|
|
222
|
+
},
|
|
223
|
+
);
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`RouteView.Match` accepts an optional `exact` prop for strict segment matching:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
h(
|
|
230
|
+
RouteView.Match,
|
|
231
|
+
{ segment: "users", exact: true },
|
|
232
|
+
{ default: () => h(UsersIndex) },
|
|
233
|
+
);
|
|
234
|
+
// Only matches "users" exactly, not "users.profile"
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**`keepAlive` prop:** Vue's native `<KeepAlive>` preserves component state across navigations. Each segment gets a dedicated wrapper component so `<KeepAlive>` can track them independently.
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
h(
|
|
241
|
+
RouteView,
|
|
242
|
+
{ nodeName: "", keepAlive: true },
|
|
243
|
+
{
|
|
244
|
+
default: () => [
|
|
245
|
+
h(RouteView.Match, { segment: "users" }, { default: () => h(UsersPage) }),
|
|
246
|
+
// UsersPage stays alive when navigating to settings and back
|
|
247
|
+
],
|
|
248
|
+
},
|
|
249
|
+
);
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Lazy loading with `fallback`:** Pass a `fallback` prop (`VNode | (() => VNode)`) to wrap the matched content in Vue's `<Suspense>`. This lets you show a loading state while a `defineAsyncComponent` chunk is fetching. Works with both `keepAlive` and non-`keepAlive` modes.
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import { defineAsyncComponent, h } from "vue";
|
|
256
|
+
|
|
257
|
+
const LazyDashboard = defineAsyncComponent(() => import("./Dashboard.vue"));
|
|
258
|
+
|
|
259
|
+
h(
|
|
260
|
+
RouteView,
|
|
261
|
+
{ nodeName: "" },
|
|
262
|
+
{
|
|
263
|
+
default: () => [
|
|
264
|
+
h(
|
|
265
|
+
RouteView.Match,
|
|
266
|
+
{ segment: "dashboard", fallback: h(Spinner) },
|
|
267
|
+
{ default: () => h(LazyDashboard) },
|
|
268
|
+
),
|
|
269
|
+
],
|
|
270
|
+
},
|
|
271
|
+
);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
In a template:
|
|
275
|
+
|
|
276
|
+
```vue
|
|
277
|
+
<script setup>
|
|
278
|
+
import { defineAsyncComponent } from "vue";
|
|
279
|
+
const LazyDashboard = defineAsyncComponent(() => import("./Dashboard.vue"));
|
|
280
|
+
</script>
|
|
281
|
+
|
|
282
|
+
<RouteView nodeName="">
|
|
283
|
+
<RouteView.Match segment="dashboard" :fallback="SpinnerComponent">
|
|
284
|
+
<LazyDashboard />
|
|
285
|
+
</RouteView.Match>
|
|
286
|
+
</RouteView>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Without `fallback`, no `<Suspense>` boundary is added. The prop is optional.
|
|
290
|
+
|
|
291
|
+
## Directives
|
|
292
|
+
|
|
293
|
+
### `v-link`
|
|
294
|
+
|
|
295
|
+
Low-level directive for adding navigation to any element. Automatically handles click events, keyboard navigation (Enter key), and cursor styling.
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
import { vLink } from "@real-router/vue";
|
|
299
|
+
|
|
300
|
+
h("a", {
|
|
301
|
+
"v-link": { name: "users.profile", params: { id: "123" } },
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
h("button", {
|
|
305
|
+
"v-link": { name: "home" },
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
h("div", {
|
|
309
|
+
"v-link": {
|
|
310
|
+
name: "settings",
|
|
311
|
+
params: {},
|
|
312
|
+
options: { replace: true },
|
|
313
|
+
},
|
|
314
|
+
role: "link",
|
|
315
|
+
tabindex: "0",
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
In a template:
|
|
320
|
+
|
|
321
|
+
```vue
|
|
322
|
+
<a v-link="{ name: 'users.profile', params: { id: '123' } }">
|
|
323
|
+
User Profile
|
|
324
|
+
</a>
|
|
325
|
+
|
|
326
|
+
<button v-link="{ name: 'home' }">
|
|
327
|
+
Go Home
|
|
328
|
+
</button>
|
|
329
|
+
|
|
330
|
+
<div v-link="{ name: 'settings' }" role="link" tabindex="0">
|
|
331
|
+
Settings
|
|
332
|
+
</div>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Value:**
|
|
336
|
+
|
|
337
|
+
| Property | Type | Default | Description |
|
|
338
|
+
| --------- | -------- | ------- | ---------------------------------- |
|
|
339
|
+
| `name` | `string` | — | Target route name |
|
|
340
|
+
| `params` | `Params` | `{}` | Route parameters |
|
|
341
|
+
| `options` | `object` | `{}` | Navigation options (replace, etc.) |
|
|
342
|
+
|
|
343
|
+
The directive automatically sets `cursor: pointer` and adds `role="link"` + `tabindex="0"` to non-interactive elements for accessibility.
|
|
344
|
+
|
|
345
|
+
## Vue-Specific Patterns
|
|
346
|
+
|
|
347
|
+
### Refs, Not Plain Values
|
|
348
|
+
|
|
349
|
+
Unlike the React and Preact adapters, `useRoute()` and `useRouteNode()` return `ShallowRef` values. Read `.value` in script:
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
// React/Preact
|
|
353
|
+
const { route } = useRoute();
|
|
354
|
+
console.log(route?.name);
|
|
355
|
+
|
|
356
|
+
// Vue
|
|
357
|
+
const { route } = useRoute();
|
|
358
|
+
console.log(route.value?.name); // .value in script
|
|
359
|
+
|
|
360
|
+
// In template — Vue auto-unwraps
|
|
361
|
+
// <div>{{ route?.name }}</div>
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Watching Route Changes
|
|
365
|
+
|
|
366
|
+
Use Vue's `watch` to react to route changes in script:
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
const { route } = useRouteNode("users");
|
|
370
|
+
|
|
371
|
+
watch(route, (newRoute) => {
|
|
372
|
+
if (newRoute) {
|
|
373
|
+
document.title = `Users — ${newRoute.params.id ?? "list"}`;
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### No .vue SFC Required
|
|
379
|
+
|
|
380
|
+
All components are plain `.ts` files using `defineComponent` + `h()`. You can use them in `.vue` SFC templates or in render functions — both work.
|
|
381
|
+
|
|
382
|
+
## Accessibility
|
|
383
|
+
|
|
384
|
+
Enable screen reader announcements for route changes:
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
h(
|
|
388
|
+
RouterProvider,
|
|
389
|
+
{ router, announceNavigation: true },
|
|
390
|
+
{
|
|
391
|
+
default: () => [
|
|
392
|
+
/* Your app */
|
|
393
|
+
],
|
|
394
|
+
},
|
|
395
|
+
);
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
Or in a template:
|
|
399
|
+
|
|
400
|
+
```vue
|
|
401
|
+
<RouterProvider :router="router" :announceNavigation="true">
|
|
402
|
+
<!-- Your app -->
|
|
403
|
+
</RouterProvider>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
When enabled, a visually hidden `aria-live` region announces each navigation. Focus moves to the first `<h1>` on the new page. See [Accessibility guide](https://github.com/greydragon888/real-router/wiki/Accessibility) for details.
|
|
407
|
+
|
|
408
|
+
## Documentation
|
|
409
|
+
|
|
410
|
+
Full documentation: [Wiki](https://github.com/greydragon888/real-router/wiki)
|
|
411
|
+
|
|
412
|
+
- [RouterProvider](https://github.com/greydragon888/real-router/wiki/RouterProvider) · [RouteView](https://github.com/greydragon888/real-router/wiki/RouteView) · [Link](https://github.com/greydragon888/real-router/wiki/Link)
|
|
413
|
+
- [useRouter](https://github.com/greydragon888/real-router/wiki/useRouter) · [useRoute](https://github.com/greydragon888/real-router/wiki/useRoute) · [useRouteNode](https://github.com/greydragon888/real-router/wiki/useRouteNode) · [useNavigator](https://github.com/greydragon888/real-router/wiki/useNavigator) · [useRouteUtils](https://github.com/greydragon888/real-router/wiki/useRouteUtils) · [useRouterTransition](https://github.com/greydragon888/real-router/wiki/useRouterTransition)
|
|
414
|
+
|
|
415
|
+
## Related Packages
|
|
416
|
+
|
|
417
|
+
| Package | Description |
|
|
418
|
+
| ---------------------------------------------------------------------------------------- | ------------------------------------ |
|
|
419
|
+
| [@real-router/core](https://www.npmjs.com/package/@real-router/core) | Core router (required dependency) |
|
|
420
|
+
| [@real-router/browser-plugin](https://www.npmjs.com/package/@real-router/browser-plugin) | Browser History API integration |
|
|
421
|
+
| [@real-router/sources](https://www.npmjs.com/package/@real-router/sources) | Subscription layer (used internally) |
|
|
422
|
+
| [@real-router/route-utils](https://www.npmjs.com/package/@real-router/route-utils) | Route tree queries (`useRouteUtils`) |
|
|
423
|
+
|
|
424
|
+
## Contributing
|
|
425
|
+
|
|
426
|
+
See [contributing guidelines](../../CONTRIBUTING.md) for development setup and PR process.
|
|
427
|
+
|
|
428
|
+
## License
|
|
429
|
+
|
|
430
|
+
[MIT](../../LICENSE) © [Oleg Ivanov](https://github.com/greydragon888)
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import * as vue from 'vue';
|
|
2
|
+
import { VNode, PropType, Directive, ShallowRef, Plugin, InjectionKey } from 'vue';
|
|
3
|
+
import { Params, NavigationOptions, Router, Navigator, State } from '@real-router/core';
|
|
4
|
+
export { Navigator } from '@real-router/core';
|
|
5
|
+
import { RouteUtils } from '@real-router/route-utils';
|
|
6
|
+
import { RouterTransitionSnapshot } from '@real-router/sources';
|
|
7
|
+
export { RouterTransitionSnapshot } from '@real-router/sources';
|
|
8
|
+
|
|
9
|
+
interface RouteViewProps {
|
|
10
|
+
readonly nodeName: string;
|
|
11
|
+
readonly keepAlive?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface MatchProps {
|
|
14
|
+
readonly segment: string;
|
|
15
|
+
readonly exact?: boolean;
|
|
16
|
+
readonly fallback?: VNode | (() => VNode);
|
|
17
|
+
}
|
|
18
|
+
type NotFoundProps = Record<string, never>;
|
|
19
|
+
|
|
20
|
+
declare const RouteView: {
|
|
21
|
+
new (...args: any[]): vue.CreateComponentPublicInstanceWithMixins<Readonly<vue.ExtractPropTypes<{
|
|
22
|
+
nodeName: {
|
|
23
|
+
type: StringConstructor;
|
|
24
|
+
required: true;
|
|
25
|
+
};
|
|
26
|
+
keepAlive: {
|
|
27
|
+
type: BooleanConstructor;
|
|
28
|
+
default: boolean;
|
|
29
|
+
};
|
|
30
|
+
}>> & Readonly<{}>, () => VNode | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, vue.PublicProps, {
|
|
31
|
+
keepAlive: boolean;
|
|
32
|
+
}, true, {}, {}, vue.GlobalComponents, vue.GlobalDirectives, string, {}, any, vue.ComponentProvideOptions, {
|
|
33
|
+
P: {};
|
|
34
|
+
B: {};
|
|
35
|
+
D: {};
|
|
36
|
+
C: {};
|
|
37
|
+
M: {};
|
|
38
|
+
Defaults: {};
|
|
39
|
+
}, Readonly<vue.ExtractPropTypes<{
|
|
40
|
+
nodeName: {
|
|
41
|
+
type: StringConstructor;
|
|
42
|
+
required: true;
|
|
43
|
+
};
|
|
44
|
+
keepAlive: {
|
|
45
|
+
type: BooleanConstructor;
|
|
46
|
+
default: boolean;
|
|
47
|
+
};
|
|
48
|
+
}>> & Readonly<{}>, () => VNode | null, {}, {}, {}, {
|
|
49
|
+
keepAlive: boolean;
|
|
50
|
+
}>;
|
|
51
|
+
__isFragment?: never;
|
|
52
|
+
__isTeleport?: never;
|
|
53
|
+
__isSuspense?: never;
|
|
54
|
+
} & vue.ComponentOptionsBase<Readonly<vue.ExtractPropTypes<{
|
|
55
|
+
nodeName: {
|
|
56
|
+
type: StringConstructor;
|
|
57
|
+
required: true;
|
|
58
|
+
};
|
|
59
|
+
keepAlive: {
|
|
60
|
+
type: BooleanConstructor;
|
|
61
|
+
default: boolean;
|
|
62
|
+
};
|
|
63
|
+
}>> & Readonly<{}>, () => VNode | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, {
|
|
64
|
+
keepAlive: boolean;
|
|
65
|
+
}, {}, string, {}, vue.GlobalComponents, vue.GlobalDirectives, string, vue.ComponentProvideOptions> & vue.VNodeProps & vue.AllowedComponentProps & vue.ComponentCustomProps & {
|
|
66
|
+
Match: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
67
|
+
segment: {
|
|
68
|
+
type: vue.PropType<string>;
|
|
69
|
+
required: true;
|
|
70
|
+
};
|
|
71
|
+
exact: {
|
|
72
|
+
type: BooleanConstructor;
|
|
73
|
+
default: boolean;
|
|
74
|
+
};
|
|
75
|
+
fallback: {
|
|
76
|
+
type: vue.PropType<VNode | (() => VNode)>;
|
|
77
|
+
default: undefined;
|
|
78
|
+
};
|
|
79
|
+
}>, {}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
80
|
+
segment: {
|
|
81
|
+
type: vue.PropType<string>;
|
|
82
|
+
required: true;
|
|
83
|
+
};
|
|
84
|
+
exact: {
|
|
85
|
+
type: BooleanConstructor;
|
|
86
|
+
default: boolean;
|
|
87
|
+
};
|
|
88
|
+
fallback: {
|
|
89
|
+
type: vue.PropType<VNode | (() => VNode)>;
|
|
90
|
+
default: undefined;
|
|
91
|
+
};
|
|
92
|
+
}>> & Readonly<{}>, {
|
|
93
|
+
exact: boolean;
|
|
94
|
+
fallback: VNode<vue.RendererNode, vue.RendererElement, {
|
|
95
|
+
[key: string]: any;
|
|
96
|
+
}> | (() => VNode);
|
|
97
|
+
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
98
|
+
NotFound: vue.DefineComponent<{}, {}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
declare const Link: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
102
|
+
routeName: {
|
|
103
|
+
type: StringConstructor;
|
|
104
|
+
required: true;
|
|
105
|
+
};
|
|
106
|
+
routeParams: {
|
|
107
|
+
type: PropType<Params>;
|
|
108
|
+
default: () => Readonly<{}>;
|
|
109
|
+
};
|
|
110
|
+
routeOptions: {
|
|
111
|
+
type: PropType<NavigationOptions>;
|
|
112
|
+
default: () => Readonly<{}>;
|
|
113
|
+
};
|
|
114
|
+
class: {
|
|
115
|
+
type: StringConstructor;
|
|
116
|
+
default: undefined;
|
|
117
|
+
};
|
|
118
|
+
activeClassName: {
|
|
119
|
+
type: StringConstructor;
|
|
120
|
+
default: string;
|
|
121
|
+
};
|
|
122
|
+
activeStrict: {
|
|
123
|
+
type: BooleanConstructor;
|
|
124
|
+
default: boolean;
|
|
125
|
+
};
|
|
126
|
+
ignoreQueryParams: {
|
|
127
|
+
type: BooleanConstructor;
|
|
128
|
+
default: boolean;
|
|
129
|
+
};
|
|
130
|
+
target: {
|
|
131
|
+
type: StringConstructor;
|
|
132
|
+
default: undefined;
|
|
133
|
+
};
|
|
134
|
+
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
135
|
+
[key: string]: any;
|
|
136
|
+
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
137
|
+
routeName: {
|
|
138
|
+
type: StringConstructor;
|
|
139
|
+
required: true;
|
|
140
|
+
};
|
|
141
|
+
routeParams: {
|
|
142
|
+
type: PropType<Params>;
|
|
143
|
+
default: () => Readonly<{}>;
|
|
144
|
+
};
|
|
145
|
+
routeOptions: {
|
|
146
|
+
type: PropType<NavigationOptions>;
|
|
147
|
+
default: () => Readonly<{}>;
|
|
148
|
+
};
|
|
149
|
+
class: {
|
|
150
|
+
type: StringConstructor;
|
|
151
|
+
default: undefined;
|
|
152
|
+
};
|
|
153
|
+
activeClassName: {
|
|
154
|
+
type: StringConstructor;
|
|
155
|
+
default: string;
|
|
156
|
+
};
|
|
157
|
+
activeStrict: {
|
|
158
|
+
type: BooleanConstructor;
|
|
159
|
+
default: boolean;
|
|
160
|
+
};
|
|
161
|
+
ignoreQueryParams: {
|
|
162
|
+
type: BooleanConstructor;
|
|
163
|
+
default: boolean;
|
|
164
|
+
};
|
|
165
|
+
target: {
|
|
166
|
+
type: StringConstructor;
|
|
167
|
+
default: undefined;
|
|
168
|
+
};
|
|
169
|
+
}>> & Readonly<{}>, {
|
|
170
|
+
class: string;
|
|
171
|
+
ignoreQueryParams: boolean;
|
|
172
|
+
routeParams: Params;
|
|
173
|
+
routeOptions: NavigationOptions;
|
|
174
|
+
activeClassName: string;
|
|
175
|
+
activeStrict: boolean;
|
|
176
|
+
target: string;
|
|
177
|
+
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
178
|
+
|
|
179
|
+
interface LinkDirectiveValue {
|
|
180
|
+
name: string;
|
|
181
|
+
params?: Params;
|
|
182
|
+
options?: NavigationOptions;
|
|
183
|
+
}
|
|
184
|
+
declare const vLink: Directive<HTMLElement, LinkDirectiveValue>;
|
|
185
|
+
|
|
186
|
+
declare const useRouter: () => Router;
|
|
187
|
+
|
|
188
|
+
declare const useNavigator: () => Navigator;
|
|
189
|
+
|
|
190
|
+
declare const useRouteUtils: () => RouteUtils;
|
|
191
|
+
|
|
192
|
+
interface RouteContext {
|
|
193
|
+
navigator: Navigator;
|
|
194
|
+
route: ShallowRef<State | undefined>;
|
|
195
|
+
previousRoute: ShallowRef<State | undefined>;
|
|
196
|
+
}
|
|
197
|
+
interface LinkProps<P extends Params = Params> {
|
|
198
|
+
routeName: string;
|
|
199
|
+
routeParams?: P;
|
|
200
|
+
routeOptions?: NavigationOptions;
|
|
201
|
+
class?: string;
|
|
202
|
+
activeClassName?: string;
|
|
203
|
+
activeStrict?: boolean;
|
|
204
|
+
ignoreQueryParams?: boolean;
|
|
205
|
+
target?: string;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
declare const useRoute: () => RouteContext;
|
|
209
|
+
|
|
210
|
+
declare function useRouteNode(nodeName: string): RouteContext;
|
|
211
|
+
|
|
212
|
+
declare function useRouterTransition(): ShallowRef<RouterTransitionSnapshot>;
|
|
213
|
+
|
|
214
|
+
declare function createRouterPlugin(router: Router): Plugin<[]>;
|
|
215
|
+
|
|
216
|
+
declare const RouterProvider: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
217
|
+
router: {
|
|
218
|
+
type: PropType<Router>;
|
|
219
|
+
required: true;
|
|
220
|
+
};
|
|
221
|
+
announceNavigation: {
|
|
222
|
+
type: BooleanConstructor;
|
|
223
|
+
default: boolean;
|
|
224
|
+
};
|
|
225
|
+
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
226
|
+
[key: string]: any;
|
|
227
|
+
}>[] | undefined, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
228
|
+
router: {
|
|
229
|
+
type: PropType<Router>;
|
|
230
|
+
required: true;
|
|
231
|
+
};
|
|
232
|
+
announceNavigation: {
|
|
233
|
+
type: BooleanConstructor;
|
|
234
|
+
default: boolean;
|
|
235
|
+
};
|
|
236
|
+
}>> & Readonly<{}>, {
|
|
237
|
+
announceNavigation: boolean;
|
|
238
|
+
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
239
|
+
|
|
240
|
+
declare const RouterKey: InjectionKey<Router>;
|
|
241
|
+
declare const NavigatorKey: InjectionKey<Navigator>;
|
|
242
|
+
declare const RouteKey: InjectionKey<RouteContext>;
|
|
243
|
+
|
|
244
|
+
export { Link, type LinkDirectiveValue, type LinkProps, NavigatorKey, RouteKey, RouteView, type MatchProps as RouteViewMatchProps, type NotFoundProps as RouteViewNotFoundProps, type RouteViewProps, RouterKey, RouterProvider, createRouterPlugin, useNavigator, useRoute, useRouteNode, useRouteUtils, useRouter, useRouterTransition, vLink };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=require("vue"),t=require("@real-router/core"),r=require("@real-router/route-utils"),o=require("@real-router/sources"),n=require("dom-utils"),u=require("@real-router/core/api");function a(){return null}var s=e.defineComponent({name:"RouteView.Match",props:{segment:{type:String,required:!0},exact:{type:Boolean,default:!1},fallback:{type:[Object,Function],default:void 0}},render:a}),i=e.defineComponent({name:"RouteView.NotFound",render:a});function c(e,t,o){return o?e===t:r.startsWithSegment(e,t)}function l(t){if(Array.isArray(t)){const r=[];for(const o of t)Array.isArray(o)?r.push(...l(o)):e.isVNode(o)&&r.push(o);return r}return e.isVNode(t)?[t]:[]}function p(t,r){const o=l(t);for(const t of o)t.type===s||t.type===i?r.push(t):t.type===e.Fragment&&p(t.children,r)}function d(t){const r=e.shallowRef(t.getSnapshot()),o=t.subscribe(()=>{r.value=t.getSnapshot()});return e.onScopeDispose(o),r}var f=Symbol("RouterKey"),v=Symbol("NavigatorKey"),m=Symbol("RouteKey"),g=()=>{const t=e.inject(f);if(!t)throw new Error("useRouter must be used within a RouterProvider");return t};function y(r){const n=g(),u=d(o.createRouteNodeSource(n,r)),a=t.getNavigator(n),s=e.shallowRef(u.value.route),i=e.shallowRef(u.value.previousRoute);return e.watch(u,e=>{s.value=e.route,i.value=e.previousRoute},{flush:"sync"}),{navigator:a,route:s,previousRoute:i}}function R(e){const t=e.children;return t?.default?.()??null}function h(t,r){if(void 0===r)return t;const o="function"==typeof r?r():r;return e.h(e.Suspense,{},{default:()=>t,fallback:()=>o})}var N=e.defineComponent({name:"RouteView",props:{nodeName:{type:String,required:!0},keepAlive:{type:Boolean,default:!1}},setup(r,{slots:o}){const n=y(r.nodeName),u=new Map;return()=>{const a=n.route.value;if(!a)return null;const l=[];p(o.default?.(),l);const{rendered:d,fallback:f}=function(e,r,o){let n,u=null,a=!1;const s=[];for(const t of e){if(t.type===i){u=t.children;continue}const e=t.props,l=e?.segment??"";!a&&c(r,o?`${o}.${l}`:l,e?.exact??!1)&&(a=!0,n=e?.fallback,s.push(t))}if(!a&&r===t.UNKNOWN_ROUTE&&null!==u){const t=e.filter(e=>e.type===i).at(-1);t&&s.push(t)}return{rendered:s,activeMatchFound:a,fallback:n}}(l,a.name,r.nodeName);if(0===d.length)return null;const v=d[0];if(!r.keepAlive){if(v.type===s||v.type===i){const t=R(v);return t?h(e.h(e.Fragment,t),f):null}return null}const m=v.props,g=m?.segment??"__not-found__",y=function(t,r){const o=t.get(r);if(o)return o;const n=e.markRaw(e.defineComponent({name:`KeepAlive-${r}`,setup:(e,t)=>()=>t.slots.default?.()}));return t.set(r,n),n}(u,g),N=R(v)??[];return h(e.h(e.KeepAlive,null,{default:()=>e.h(y,{key:g},{default:()=>N})}),f)}}}),w=Object.assign(N,{Match:s,NotFound:i}),b=Object.freeze({}),k=Object.freeze({}),S=e.defineComponent({name:"Link",props:{routeName:{type:String,required:!0},routeParams:{type:Object,default:()=>b},routeOptions:{type:Object,default:()=>k},class:{type:String,default:void 0},activeClassName:{type:String,default:"active"},activeStrict:{type:Boolean,default:!1},ignoreQueryParams:{type:Boolean,default:!0},target:{type:String,default:void 0}},setup(t,{slots:r,attrs:u}){const a=g(),s=function(e,t,r=!1,n=!0){const u=g();return d(o.createActiveRouteSource(u,e,t,{strict:r,ignoreQueryParams:n}))}(t.routeName,t.routeParams,t.activeStrict,t.ignoreQueryParams),i=e.computed(()=>n.buildHref(a,t.routeName,t.routeParams)),c=e.computed(()=>n.buildActiveClassName(s.value,t.activeClassName,t.class)),l=e=>{u.onClick&&"function"==typeof u.onClick&&(u.onClick(e),e.defaultPrevented)||n.shouldNavigate(e)&&"_blank"!==t.target&&(e.preventDefault(),a.navigate(t.routeName,t.routeParams,t.routeOptions).catch(()=>{}))};return()=>e.h("a",{...u,href:i.value,class:c.value,target:t.target,onClick:l},r.default?.())}}),P=null;function x(e){P=e}function A(){if(!P)throw new Error("v-link directive requires a RouterProvider ancestor. Make sure RouterProvider is mounted.");return P}var C=new WeakMap,q=new WeakMap;function E(e,t,r){const o=function(e,t){return r=>{n.shouldNavigate(r)&&(r.preventDefault(),e.navigate(t.name,t.params??{},t.options??{}).catch(()=>{}))}}(t,r),u=function(e,t,r){return o=>{"Enter"!==o.key||r instanceof HTMLButtonElement||e.navigate(t.name,t.params??{},t.options??{}).catch(()=>{})}}(t,r,e);e.addEventListener("click",o),e.addEventListener("keydown",u),C.set(e,o),q.set(e,u)}function O(e){const t=C.get(e),r=q.get(e);t&&e.removeEventListener("click",t),r&&e.removeEventListener("keydown",r),C.delete(e),q.delete(e)}var j={mounted(e,t){const r=A();n.applyLinkA11y(e),e.style.cursor="pointer",E(e,r,t.value)},updated(e,t){const r=A();O(e),E(e,r,t.value)},beforeUnmount(e){O(e)}},K=e.defineComponent({name:"RouterProvider",props:{router:{type:Object,required:!0},announceNavigation:{type:Boolean,default:!1}},setup(r,{slots:u}){e.onMounted(()=>{if(!r.announceNavigation)return;const t=n.createRouteAnnouncer(r.router);e.onUnmounted(()=>{t.destroy()})});const a=t.getNavigator(r.router);x(r.router);const s=o.createRouteSource(r.router),i=s.getSnapshot(),c=e.shallowRef(i.route),l=e.shallowRef(i.previousRoute),p=s.subscribe(()=>{const e=s.getSnapshot();c.value=e.route,l.value=e.previousRoute});return e.onScopeDispose(p),e.provide(f,r.router),e.provide(v,a),e.provide(m,{navigator:a,route:c,previousRoute:l}),()=>u.default?.()}});exports.Link=S,exports.NavigatorKey=v,exports.RouteKey=m,exports.RouteView=w,exports.RouterKey=f,exports.RouterProvider=K,exports.createRouterPlugin=function(r){return{install(n){const u=t.getNavigator(r);x(r);const a=o.createRouteSource(r),s=a.getSnapshot(),i=e.shallowRef(s.route),c=e.shallowRef(s.previousRoute);a.subscribe(()=>{const e=a.getSnapshot();i.value=e.route,c.value=e.previousRoute}),n.provide(f,r),n.provide(v,u),n.provide(m,{navigator:u,route:i,previousRoute:c})}}},exports.useNavigator=()=>{const t=e.inject(v);if(!t)throw new Error("useNavigator must be used within a RouterProvider");return t},exports.useRoute=()=>{const t=e.inject(m);if(!t)throw new Error("useRoute must be used within a RouterProvider");return t},exports.useRouteNode=y,exports.useRouteUtils=()=>{const e=g();return r.getRouteUtils(u.getPluginApi(e).getTree())},exports.useRouter=g,exports.useRouterTransition=function(){const e=g();return d(o.createTransitionSource(e))},exports.vLink=j;//# sourceMappingURL=index.js.map
|