vue-multi-router 0.1.0 → 0.1.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 CHANGED
@@ -25,19 +25,214 @@ npm install vue-multi-router
25
25
 
26
26
  ## Features
27
27
 
28
- wip
28
+ - **Multiple Independent Routers** - Run multiple Vue Router instances simultaneously in a single app
29
+ - **Context-Based Navigation** - Each routing context maintains its own navigation history
30
+ - **Browser History Integration** - Back/forward buttons work across contexts with proper URL updates
31
+ - **Session Persistence** - Context states persist across page reloads via SessionStorage or other implementations
32
+ - **TypeScript Support** - Full type definitions included
33
+ - **Composable API** - Easy-to-use composables for accessing router state
29
34
 
30
35
  ## Basic Usage
31
36
 
32
- wip
37
+ ### 1. Create Multi Router
38
+
39
+ ```typescript
40
+ // router.ts
41
+ import { createMultiRouter } from 'vue-multi-router'
42
+ import { createWebHistory } from 'vue-router'
43
+
44
+ export const multiRouter = createMultiRouter({
45
+ history: () => createWebHistory(),
46
+ routes: [
47
+ {
48
+ path: '/',
49
+ component: () => import('./views/Layout.vue'),
50
+ children: [
51
+ { path: 'home', component: () => import('./views/Home.vue') },
52
+ { path: 'about', component: () => import('./views/About.vue') },
53
+ ],
54
+ },
55
+ ],
56
+ })
57
+ ```
58
+
59
+ ### 2. Install Plugin
60
+
61
+ ```typescript
62
+ // main.ts
63
+ import { createApp } from 'vue'
64
+ import App from './App.vue'
65
+ import { multiRouter } from './router'
66
+
67
+ const app = createApp(App)
68
+ app.use(multiRouter)
69
+ app.mount('#app')
70
+ ```
71
+
72
+ ### 3. Define Contexts
73
+
74
+ ```vue
75
+ <!-- App.vue -->
76
+ <template>
77
+ <MultiRouterContext type="main" name="main" default>
78
+ <RouterView />
79
+ </MultiRouterContext>
80
+ </template>
81
+
82
+ <script setup>
83
+ import { MultiRouterContext } from 'vue-multi-router'
84
+ </script>
85
+ ```
86
+
87
+ `MultiRouterContext` acts as an activator by default — clicking anywhere inside it activates the context. No extra wrapper needed.
88
+
89
+ ### 4. Create Additional Contexts
90
+
91
+ ```vue
92
+ <!-- Panels.vue -->
93
+ <template>
94
+ <div v-for="panel in panels" :key="panel.id">
95
+ <MultiRouterContext
96
+ type="panel"
97
+ :name="`panel-${panel.id}`"
98
+ initial-location="/home"
99
+ >
100
+ <RouterView />
101
+ </MultiRouterContext>
102
+ </div>
103
+ </template>
104
+ ```
33
105
 
34
106
  ## API Reference
35
107
 
36
- wip
108
+ ### `createMultiRouter(options)`
109
+
110
+ Creates a multi-router instance.
111
+
112
+ **Options:**
113
+ - `history: () => RouterHistory` - Factory function returning a Vue Router history instance
114
+ - `routes: RouteRecordRaw[]` - Route definitions (same as Vue Router)
115
+ - `historyOptions?: MultiRouterHistoryManagerOptions` - History management options
116
+
117
+ ### Route Meta Options
118
+
119
+ **`multiRouterRoot: boolean`**
120
+
121
+ Marks a route as the root for context rendering. When a context navigates to a nested route, `RouterView` inside the context will start rendering from the route marked with `multiRouterRoot: true`, skipping parent routes.
122
+
123
+ ```typescript
124
+ const routes = [
125
+ {
126
+ path: '/dashboard',
127
+ component: DashboardLayout,
128
+ children: [
129
+ {
130
+ path: 'panels',
131
+ component: PanelsContainer,
132
+ children: [
133
+ {
134
+ path: 'content',
135
+ component: PanelContent,
136
+ meta: { multiRouterRoot: true }, // Context renders from here
137
+ },
138
+ ],
139
+ },
140
+ ],
141
+ },
142
+ ]
143
+ ```
144
+
145
+ This is useful when contexts are nested inside shared layouts but should render independently from their root component.
146
+
147
+ ### `<MultiRouterContext>`
148
+
149
+ Component that defines a routing context boundary. By default, it also acts as an activator — clicking inside the context activates it.
150
+
151
+ **Props:**
152
+ - `type: string` - Context type identifier (for debugging/organization)
153
+ - `name: string` - Unique context identifier
154
+ - `location?: string` - Force specific location (overrides storage)
155
+ - `initial-location?: string` - Initial location for new contexts
156
+ - `history-enabled?: boolean` - Whether to track in browser history (default: `true`)
157
+ - `default?: boolean` - Activate by default if no saved context exists
158
+ - `activator?: boolean` - Whether to activate context on mousedown (default: `true`). Set to `false` to opt out of built-in activation behavior
159
+ - `prevent-class?: string` - CSS class that prevents activation on click, useful if you want to prevent activation on click of a button that destroys the context
160
+
161
+ To disable the built-in activator:
162
+
163
+ ```vue
164
+ <MultiRouterContext type="panel" name="panel-1" :activator="false">
165
+ <!-- manage activation manually -->
166
+ </MultiRouterContext>
167
+ ```
168
+
169
+ ### `<MultiRouterContextActivator>`
170
+
171
+ Standalone wrapper component that activates context on user interaction. Useful for advanced cases where you need fine-grained control over which element triggers activation, separate from the `MultiRouterContext` boundary.
172
+
173
+ **Props:**
174
+ - `prevent-class?: string` - CSS class that prevents activation on click
175
+ - `as?: string` - HTML element to render as wrapper (default: fragment/div)
176
+
177
+ ### `useMultiRouter()`
178
+
179
+ Composable for accessing multi-router outside a context.
180
+
181
+ **Returns:**
182
+ - `activeContextKey: ComputedRef<string | undefined>` - Currently active context
183
+ - `activeHistoryContextKey: ComputedRef<string | undefined>` - Context controlling browser URL
184
+ - `setActive(contextKey: string, updateHistory?: boolean): void` - Activate a context
185
+ - `hasContext(contextKey: string): boolean` - Check if context exists
186
+
187
+ ### `useMultiRouterContext()`
188
+
189
+ Composable for use inside a `MultiRouterContext`.
190
+
191
+ **Returns:**
192
+ - `router: ComputedRef<Router>` - Vue Router instance for this context
193
+ - `route: ComputedRef<RouteLocationNormalized>` - Current route
194
+ - `isActive: ComputedRef<boolean>` - Whether this context is active
195
+ - `isHistoryActive: ComputedRef<boolean>` - Whether this context controls browser URL
196
+ - `activate(updateHistory?: boolean): void` - Activate this context
197
+ - `contextKey: string` - This context's key
37
198
 
38
199
  ## Examples
39
200
 
40
- wip
201
+ ### Cards with Query Parameters
202
+
203
+ ```vue
204
+ <template>
205
+ <div v-for="card in cards" :key="card.id">
206
+ <MultiRouterContext
207
+ type="card"
208
+ :name="`card-${card.id}`"
209
+ initial-location="/card/content"
210
+ :history-enabled="false"
211
+ >
212
+ <CardContent @close="removeCard(card.id)" />
213
+ </MultiRouterContext>
214
+ </div>
215
+ </template>
216
+ ```
217
+
218
+ ### Accessing Route in Context
219
+
220
+ ```vue
221
+ <script setup>
222
+ import { useRouter, useRoute } from 'vue-router'
223
+
224
+ const router = useRouter()
225
+ const route = useRoute()
226
+
227
+ // Navigate within this context
228
+ function goToPage(path) {
229
+ router.push(path)
230
+ }
231
+
232
+ // Access query params
233
+ const searchQuery = computed(() => route.query.q)
234
+ </script>
235
+ ```
41
236
 
42
237
  ## Peer Dependencies
43
238
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- import * as vue6 from "vue";
1
+ import * as vue0 from "vue";
2
2
  import { App, InjectionKey, PropType } from "vue";
3
- import * as vue_router0 from "vue-router";
4
3
  import { Router, RouterHistory, RouterOptions } from "vue-router";
5
4
 
6
5
  //#region src/history/types.d.ts
@@ -48,8 +47,26 @@ interface ContextStorageAdapter {
48
47
  clearStack(contextKey: string): MaybePromise<void>;
49
48
  saveActiveContext(contextKey: string): MaybePromise<void>;
50
49
  getActiveContext(): MaybePromise<string | null>;
50
+ clearActiveContext(): MaybePromise<void>;
51
51
  saveActiveHistoryContext(contextKey: string): MaybePromise<void>;
52
52
  getActiveHistoryContext(): MaybePromise<string | null>;
53
+ clearActiveHistoryContext(): MaybePromise<void>;
54
+ /**
55
+ * Save the stack of previously active contexts (for fallback when closing a context)
56
+ */
57
+ saveContextStack(stack: string[]): MaybePromise<void>;
58
+ /**
59
+ * Get the stack of previously active contexts
60
+ */
61
+ getContextStack(): MaybePromise<string[]>;
62
+ /**
63
+ * Save the stack of previously active history contexts (for fallback when closing a context)
64
+ */
65
+ saveHistoryContextStack(stack: string[]): MaybePromise<void>;
66
+ /**
67
+ * Get the stack of previously active history contexts
68
+ */
69
+ getHistoryContextStack(): MaybePromise<string[]>;
53
70
  }
54
71
  //#endregion
55
72
  //#region src/history/manager.d.ts
@@ -90,10 +107,12 @@ declare class MultiRouterHistoryManager {
90
107
  private stacks;
91
108
  private activeHistoryContextKey;
92
109
  private historyContextStack;
110
+ private contextStack;
93
111
  private baseHistoryCleanup;
94
112
  private contextSwitchMode;
95
113
  private onContextActivate?;
96
114
  constructor(historyBuilder: HistoryBuilder, options?: MultiRouterHistoryManagerOptions);
115
+ private restoreContextStacks;
97
116
  get base(): string;
98
117
  get location(): HistoryLocation;
99
118
  get state(): HistoryState;
@@ -108,6 +127,24 @@ declare class MultiRouterHistoryManager {
108
127
  setActiveHistoryContext(contextKey: string): void;
109
128
  clearActiveHistoryContext(contextKey: string): void;
110
129
  saveActiveContext(contextKey: string): void;
130
+ /**
131
+ * Push the current active context to the context stack before switching to a new one.
132
+ * This allows fallback to the previous context when the current one is removed.
133
+ */
134
+ pushToContextStack(contextKey: string): void;
135
+ /**
136
+ * Get the previous context from the stack (for fallback when current context is removed).
137
+ * Returns null if no previous context exists.
138
+ */
139
+ popFromContextStack(): string | null;
140
+ /**
141
+ * Remove a context from the context stack (when the context is unregistered).
142
+ */
143
+ removeFromContextStack(contextKey: string): void;
144
+ /**
145
+ * Clear the active context from storage (when no contexts are left).
146
+ */
147
+ clearActiveContext(): void;
111
148
  getActiveHistoryContextKey(): string | null;
112
149
  private fallbackToPreviousHistoryContext;
113
150
  private createInitialVirtualStack;
@@ -124,71 +161,23 @@ declare class MultiRouterHistoryManager {
124
161
  getLastActiveContextKey(): MaybePromise<string | null>;
125
162
  }
126
163
  //#endregion
127
- //#region src/types.d.ts
128
- type Wip = true;
129
- interface ContextTypeOptions {
130
- canUseAsHistoryContext: boolean;
131
- single: boolean;
132
- }
133
- type ContextTypes = Record<string, ContextTypeOptions>;
134
- //#endregion
135
164
  //#region src/multi-router.d.ts
136
165
  type MultiRouterInterface = {
137
166
  getByContextName: (name: string) => Router;
138
167
  install: (app: App) => void;
139
168
  };
140
- declare const contextTemplateWindows: {
141
- window: {
142
- canUseAsHistoryContext: true;
143
- single: false;
144
- };
145
- };
146
- declare const contextTemplateTabs: {
147
- tab: {
148
- canUseAsHistoryContext: true;
149
- single: false;
150
- };
151
- };
152
- declare const contextTemplateMainWithWindows: {
153
- window: {
154
- canUseAsHistoryContext: true;
155
- single: false;
156
- };
157
- main: {
158
- canUseAsHistoryContext: true;
159
- single: true;
160
- };
161
- };
162
- declare const contextTemplateDesktopWithWindows: {
163
- window: {
164
- canUseAsHistoryContext: true;
165
- single: false;
166
- };
167
- desktop: {
168
- canUseAsHistoryContext: false;
169
- single: true;
170
- };
171
- };
172
- declare const contextTemplateTabsWithWindows: {
173
- window: {
174
- canUseAsHistoryContext: true;
175
- single: false;
176
- };
177
- tab: {
178
- canUseAsHistoryContext: true;
179
- single: false;
180
- };
181
- };
182
169
  type CustomRouterOptions = Omit<RouterOptions, 'history'> & {
183
170
  history: () => RouterHistory;
184
171
  historyOptions?: MultiRouterHistoryManagerOptions;
185
- types: ContextTypes;
186
172
  };
187
173
  declare function createMultiRouter(options: CustomRouterOptions): MultiRouterInterface;
188
174
  //#endregion
175
+ //#region src/hooks.d.ts
176
+ declare function onMultiRouterContextActivate(callback: (name: string) => void): void;
177
+ //#endregion
189
178
  //#region src/components/MultiRouterContext.vue.d.ts
190
179
  declare const _default: typeof __VLS_export$1;
191
- declare const __VLS_export$1: vue6.DefineComponent<vue6.ExtractPropTypes<{
180
+ declare const __VLS_export$1: vue0.DefineComponent<vue0.ExtractPropTypes<{
192
181
  type: {
193
182
  type: StringConstructor;
194
183
  required: true;
@@ -223,9 +212,27 @@ declare const __VLS_export$1: vue6.DefineComponent<vue6.ExtractPropTypes<{
223
212
  type: BooleanConstructor;
224
213
  default: boolean;
225
214
  };
226
- }>, () => vue6.VNode<vue6.RendererNode, vue6.RendererElement, {
215
+ /**
216
+ * Whether this context should act as an activator, activating the context on mousedown.
217
+ * Set false to opt out of built-in activation behavior.
218
+ * @default true
219
+ */
220
+ activator: {
221
+ type: BooleanConstructor;
222
+ default: boolean;
223
+ };
224
+ /**
225
+ * CSS class that prevents activation when the mousedown target matches.
226
+ * Only used when activator is true.
227
+ * @default null
228
+ */
229
+ preventClass: {
230
+ type: PropType<string | null>;
231
+ default: null;
232
+ };
233
+ }>, () => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
227
234
  [key: string]: any;
228
- }>, {}, {}, {}, vue6.ComponentOptionsMixin, vue6.ComponentOptionsMixin, {}, string, vue6.PublicProps, Readonly<vue6.ExtractPropTypes<{
235
+ }>, {}, {}, {}, vue0.ComponentOptionsMixin, vue0.ComponentOptionsMixin, {}, string, vue0.PublicProps, Readonly<vue0.ExtractPropTypes<{
229
236
  type: {
230
237
  type: StringConstructor;
231
238
  required: true;
@@ -260,30 +267,61 @@ declare const __VLS_export$1: vue6.DefineComponent<vue6.ExtractPropTypes<{
260
267
  type: BooleanConstructor;
261
268
  default: boolean;
262
269
  };
270
+ /**
271
+ * Whether this context should act as an activator, activating the context on mousedown.
272
+ * Set false to opt out of built-in activation behavior.
273
+ * @default true
274
+ */
275
+ activator: {
276
+ type: BooleanConstructor;
277
+ default: boolean;
278
+ };
279
+ /**
280
+ * CSS class that prevents activation when the mousedown target matches.
281
+ * Only used when activator is true.
282
+ * @default null
283
+ */
284
+ preventClass: {
285
+ type: PropType<string | null>;
286
+ default: null;
287
+ };
263
288
  }>> & Readonly<{}>, {
264
289
  historyEnabled: boolean;
265
290
  default: boolean;
266
- }, {}, {}, {}, string, vue6.ComponentProvideOptions, true, {}, any>;
291
+ activator: boolean;
292
+ preventClass: string | null;
293
+ }, {}, {}, {}, string, vue0.ComponentProvideOptions, true, {}, any>;
267
294
  //#endregion
268
295
  //#region src/components/MultiRouterContextActivator.vue.d.ts
269
296
  declare const _default$1: typeof __VLS_export;
270
- declare const __VLS_export: vue6.DefineComponent<vue6.ExtractPropTypes<{
297
+ declare const __VLS_export: vue0.DefineComponent<vue0.ExtractPropTypes<{
271
298
  as: {
272
299
  type: PropType<string | null>;
273
300
  default: null;
274
301
  };
275
- }>, (() => vue6.VNode<vue6.RendererNode, vue6.RendererElement, {
302
+ preventClass: {
303
+ type: PropType<string | null>;
304
+ default: null;
305
+ required: false;
306
+ };
307
+ }>, (() => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
276
308
  [key: string]: any;
277
- }>[] | undefined) | (() => vue6.VNode<vue6.RendererNode, vue6.RendererElement, {
309
+ }>[] | undefined) | (() => vue0.VNode<vue0.RendererNode, vue0.RendererElement, {
278
310
  [key: string]: any;
279
- }>), {}, {}, {}, vue6.ComponentOptionsMixin, vue6.ComponentOptionsMixin, {}, string, vue6.PublicProps, Readonly<vue6.ExtractPropTypes<{
311
+ }>), {}, {}, {}, vue0.ComponentOptionsMixin, vue0.ComponentOptionsMixin, {}, string, vue0.PublicProps, Readonly<vue0.ExtractPropTypes<{
280
312
  as: {
281
313
  type: PropType<string | null>;
282
314
  default: null;
283
315
  };
316
+ preventClass: {
317
+ type: PropType<string | null>;
318
+ default: null;
319
+ required: false;
320
+ };
284
321
  }>> & Readonly<{}>, {
322
+ preventClass: string | null;
285
323
  as: string | null;
286
- }, {}, {}, {}, string, vue6.ComponentProvideOptions, true, {}, any>;
324
+ }, {}, {}, {}, string, vue0.ComponentProvideOptions, true, {}, any>;
287
325
  //#endregion
288
326
  //#region src/contextManager.d.ts
289
327
  type ContextInterface = {
@@ -303,7 +341,6 @@ type HistoryManagerOptions = {
303
341
  historyBuilder: HistoryBuilder;
304
342
  } & MultiRouterHistoryManagerOptions;
305
343
  declare class MultiRouterManagerInstance {
306
- private types;
307
344
  private makeRouter;
308
345
  private started;
309
346
  private activeContext;
@@ -311,12 +348,12 @@ declare class MultiRouterManagerInstance {
311
348
  private registered;
312
349
  private onContextInitListeners;
313
350
  private readonly historyManager;
314
- constructor(app: App, types: ContextTypes, historyManagerOptions: HistoryManagerOptions, makeRouter: MakeRouterFn);
351
+ constructor(app: App, historyManagerOptions: HistoryManagerOptions, makeRouter: MakeRouterFn);
315
352
  getHistoryManager(): MultiRouterHistoryManager;
316
353
  getActiveContext(): ActiveContextInterface | undefined;
317
- getActiveContextRef(): vue6.ShallowRef<ActiveContextInterface | undefined, ActiveContextInterface | undefined>;
354
+ getActiveContextRef(): vue0.ShallowRef<ActiveContextInterface | undefined, ActiveContextInterface | undefined>;
318
355
  getActiveHistoryContext(): ActiveContextInterface | undefined;
319
- getActiveHistoryContextRef(): vue6.ShallowRef<ActiveContextInterface | undefined, ActiveContextInterface | undefined>;
356
+ getActiveHistoryContextRef(): vue0.ShallowRef<ActiveContextInterface | undefined, ActiveContextInterface | undefined>;
320
357
  setActive(key: string, updateHistory: boolean): boolean;
321
358
  clearHistoryContext(key: string): void;
322
359
  markAsStarted(): void;
@@ -331,26 +368,43 @@ declare class MultiRouterManagerInstance {
331
368
  getContextLocation(key: string): string | undefined;
332
369
  getContextHistoryEnabled(key: string): boolean;
333
370
  unregister(key: string): void;
371
+ private activateFromStackOrFallback;
372
+ private fallbackToPreviousContext;
334
373
  initialize(key: string): void;
335
374
  onContextInit(fn: ContextInitListener): void;
336
375
  }
337
376
  //#endregion
377
+ //#region src/composables/useMultiRouter.d.ts
378
+ /**
379
+ * Composable for accessing the multi-router manager outside a context.
380
+ * Use this when you need to control contexts from a parent component.
381
+ *
382
+ * For usage inside a context, use `useMultiRouterContext()` instead.
383
+ */
384
+ declare function useMultiRouter(): {
385
+ manager: MultiRouterManagerInstance;
386
+ activeContextKey: vue0.ComputedRef<string | undefined>;
387
+ activeHistoryContextKey: vue0.ComputedRef<string | undefined>;
388
+ setActive: (contextKey: string, updateHistory?: boolean) => void;
389
+ hasContext: (contextKey: string) => boolean;
390
+ };
391
+ //#endregion
338
392
  //#region src/composables/useMultiRouterContext.d.ts
339
393
  declare function useMultiRouterContext(): {
340
394
  manager: MultiRouterManagerInstance;
341
395
  contextKey: string;
342
- router: vue6.ComputedRef<Router>;
343
- route: vue6.ComputedRef<vue_router0.RouteLocationNormalizedLoadedGeneric>;
344
- isActive: vue6.ComputedRef<boolean>;
345
- isHistoryActive: vue6.ComputedRef<boolean>;
346
- activeContextKey: vue6.ComputedRef<string | undefined>;
347
- activeHistoryContextKey: vue6.ComputedRef<string | undefined>;
348
- historyEnabled: vue6.ComputedRef<boolean>;
396
+ isActive: vue0.ComputedRef<boolean>;
397
+ isHistoryActive: vue0.ComputedRef<boolean>;
398
+ activeContextKey: vue0.ComputedRef<string | undefined>;
399
+ activeHistoryContextKey: vue0.ComputedRef<string | undefined>;
400
+ historyEnabled: vue0.ComputedRef<boolean>;
349
401
  activate: (updateHistory?: boolean) => void;
402
+ setActive: (contextKey: string, updateHistory?: boolean) => void;
403
+ hasContext: (contextKey: string) => boolean;
350
404
  };
351
405
  //#endregion
352
406
  //#region src/injectionSymbols.d.ts
353
407
  declare const multiRouterContextManagerKey: InjectionKey<MultiRouterManagerInstance>;
354
408
  declare const multiRouterContextKey: InjectionKey<string>;
355
409
  //#endregion
356
- export { type ContextSwitchMode, ContextTypeOptions, ContextTypes, type HistoryBuilder, type HistoryLocation, type HistoryState, _default as MultiRouterContext, _default$1 as MultiRouterContextActivator, type MultiRouterHistoryManagerOptions, type NavigationCallback, type NavigationInformation, type VirtualStack, Wip, contextTemplateDesktopWithWindows, contextTemplateMainWithWindows, contextTemplateTabs, contextTemplateTabsWithWindows, contextTemplateWindows, createMultiRouter, multiRouterContextKey, multiRouterContextManagerKey, useMultiRouterContext };
410
+ export { type ContextSwitchMode, type HistoryBuilder, type HistoryLocation, type HistoryState, _default as MultiRouterContext, _default$1 as MultiRouterContextActivator, type MultiRouterHistoryManagerOptions, type NavigationCallback, type NavigationInformation, type VirtualStack, createMultiRouter, multiRouterContextKey, multiRouterContextManagerKey, onMultiRouterContextActivate, useMultiRouter, useMultiRouterContext };