vue3-router-tab 1.4.4 → 1.4.6
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 +39 -32
- package/dist/vue3-router-tab.css +1 -1
- package/dist/vue3-router-tab.js +523 -562
- package/dist/vue3-router-tab.umd.cjs +1 -1
- package/index.d.ts +3 -6
- package/lib/components/RouterTab.vue +15 -50
- package/lib/core/createRouterTabs.ts +7 -35
- package/lib/core/types.ts +0 -3
- package/lib/scss/index.scss +10 -2
- package/lib/useReactiveTab.ts +0 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
A powerful, feature-rich Vue 3 tab-bar plugin that keeps multiple routes alive with smooth transitions, context menus, drag-and-drop reordering, and optional cookie-based persistence. Built for modern Vue 3 applications with full TypeScript support.
|
|
8
8
|
|
|
9
|
+
## ⚠️ Breaking Change
|
|
10
|
+
|
|
11
|
+
The legacy `contextmenu` prop alias has been removed.
|
|
12
|
+
Use `contextMenu` instead.
|
|
13
|
+
|
|
9
14
|
## ✨ Key Features
|
|
10
15
|
|
|
11
16
|
- 🎯 **Multi-tab Navigation** - Keep multiple routes alive simultaneously with intelligent caching
|
|
@@ -14,7 +19,6 @@ A powerful, feature-rich Vue 3 tab-bar plugin that keeps multiple routes alive w
|
|
|
14
19
|
- 🖱️ **Context Menu** - Right-click tabs for refresh, close, and navigation options
|
|
15
20
|
- 🔀 **Drag & Drop** - Reorder tabs with drag-and-drop (sortable)
|
|
16
21
|
- 💾 **Cookie Persistence** - Restore tabs on page refresh with customizable options
|
|
17
|
-
- 📌 **Sticky Routes** - Pin selected routes so they stay open and non-closable
|
|
18
22
|
- 🎭 **Theme Support** - Light, dark, and system themes with customizable colors
|
|
19
23
|
- ⚡ **KeepAlive Support** - Preserve component state when switching tabs with smart cache management
|
|
20
24
|
- ♿ **Accessibility** - Full WCAG compliance with ARIA labels, keyboard navigation, and screen reader support
|
|
@@ -72,32 +76,6 @@ That's it! You now have a fully functional tabbed router interface.
|
|
|
72
76
|
</template>
|
|
73
77
|
```
|
|
74
78
|
|
|
75
|
-
### Sticky Tabs
|
|
76
|
-
|
|
77
|
-
Mark routes as sticky when you want them to stay pinned and non-closable:
|
|
78
|
-
|
|
79
|
-
```vue
|
|
80
|
-
<template>
|
|
81
|
-
<router-tab
|
|
82
|
-
:sticky-tabs="true"
|
|
83
|
-
:keep-alive="true"
|
|
84
|
-
/>
|
|
85
|
-
</template>
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
```ts
|
|
89
|
-
{
|
|
90
|
-
path: '/dashboard',
|
|
91
|
-
component: Dashboard,
|
|
92
|
-
meta: {
|
|
93
|
-
title: 'Dashboard',
|
|
94
|
-
sticky: true,
|
|
95
|
-
},
|
|
96
|
-
}
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Set `:sticky-tabs="false"` to ignore sticky route metadata globally.
|
|
100
|
-
|
|
101
79
|
## 📖 Usage Guide
|
|
102
80
|
|
|
103
81
|
### Basic Configuration
|
|
@@ -129,7 +107,6 @@ const routes = [
|
|
|
129
107
|
meta: {
|
|
130
108
|
title: 'Home',
|
|
131
109
|
icon: 'mdi-home',
|
|
132
|
-
sticky: true,
|
|
133
110
|
keepAlive: true,
|
|
134
111
|
},
|
|
135
112
|
},
|
|
@@ -306,7 +283,7 @@ Vue3 Router Tab is fully accessible with:
|
|
|
306
283
|
| `defaultPage` | `RouteLocationRaw` | `'/'` | Default route |
|
|
307
284
|
| `tabTransition` | `TransitionLike` | `'router-tab-zoom'` | Tab list transitions |
|
|
308
285
|
| `pageTransition` | `TransitionLike` | `{ name: 'router-tab-swap', mode: 'out-in' }` | Page transitions |
|
|
309
|
-
| `
|
|
286
|
+
| `contextMenu` | `boolean \| RouterTabsMenuConfig[]` | `true` | Context menu configuration |
|
|
310
287
|
| `cookieKey` | `string` | `'router-tabs:snapshot'` | Persistence cookie key |
|
|
311
288
|
| `persistence` | `RouterTabsPersistenceOptions \| null` | `null` | Advanced persistence options |
|
|
312
289
|
| `sortable` | `boolean` | `true` | Enable drag-and-drop sorting |
|
|
@@ -387,7 +364,7 @@ interface RouterTabsContext {
|
|
|
387
364
|
|
|
388
365
|
```vue
|
|
389
366
|
<router-tab
|
|
390
|
-
:
|
|
367
|
+
:contextMenu="[
|
|
391
368
|
'refresh',
|
|
392
369
|
'close',
|
|
393
370
|
{ id: 'duplicate', label: 'Duplicate Tab', handler: ({ target }) => openTab(target.to) },
|
|
@@ -585,6 +562,22 @@ import RouterTab from 'vue3-router-tab'
|
|
|
585
562
|
|
|
586
563
|
The API is backward compatible. New features are additive.
|
|
587
564
|
|
|
565
|
+
### Context Menu Prop Update
|
|
566
|
+
|
|
567
|
+
The legacy `contextmenu` prop alias has been removed.
|
|
568
|
+
|
|
569
|
+
Use:
|
|
570
|
+
|
|
571
|
+
```vue
|
|
572
|
+
<router-tab :contextMenu="true" />
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
Instead of:
|
|
576
|
+
|
|
577
|
+
```vue
|
|
578
|
+
<router-tab :contextmenu="true" />
|
|
579
|
+
```
|
|
580
|
+
|
|
588
581
|
## 📱 Browser Support
|
|
589
582
|
|
|
590
583
|
- **Chrome**: 90+
|
|
@@ -634,7 +627,7 @@ Built with ❤️ by the Vue.js community. Special thanks to all contributors an
|
|
|
634
627
|
| `defaultPage` | `string \| object` | `'/'` | Default route to navigate to |
|
|
635
628
|
| `tabTransition` | `string \| object` | `'router-tab-zoom'` | Transition for tab changes |
|
|
636
629
|
| `pageTransition` | `string \| object` | `{ name: 'router-tab-swap', mode: 'out-in' }` | Transition for page changes |
|
|
637
|
-
| `
|
|
630
|
+
| `contextMenu` | `boolean \| array` | `true` | Enable context menu or provide custom menu items |
|
|
638
631
|
| `cookieKey` | `string` | `'router-tabs:snapshot'` | Cookie key for persistence |
|
|
639
632
|
| `persistence` | `object \| null` | `null` | Persistence configuration |
|
|
640
633
|
| `sortable` | `boolean` | `true` | Enable drag-and-drop tab sorting |
|
|
@@ -1084,7 +1077,7 @@ Fine-grained control over tab persistence:
|
|
|
1084
1077
|
|
|
1085
1078
|
```vue
|
|
1086
1079
|
<router-tab
|
|
1087
|
-
:
|
|
1080
|
+
:contextMenu="[
|
|
1088
1081
|
'refresh',
|
|
1089
1082
|
'close',
|
|
1090
1083
|
{ id: 'closeOthers', label: 'Close All Others' },
|
|
@@ -1254,6 +1247,8 @@ The package ships with its own CSS bundle (imported automatically). Override CSS
|
|
|
1254
1247
|
:root {
|
|
1255
1248
|
/* Layout */
|
|
1256
1249
|
--router-tab-header-height: 48px;
|
|
1250
|
+
--router-tab-sticky-top: 0px;
|
|
1251
|
+
--router-tab-header-z-index: 10;
|
|
1257
1252
|
--router-tab-padding: 16px;
|
|
1258
1253
|
--router-tab-font-size: 14px;
|
|
1259
1254
|
|
|
@@ -1272,6 +1267,18 @@ The package ships with its own CSS bundle (imported automatically). Override CSS
|
|
|
1272
1267
|
}
|
|
1273
1268
|
```
|
|
1274
1269
|
|
|
1270
|
+
When `router-tab` is rendered below a fixed app header, set `--router-tab-sticky-top` so the sticky bar locks beneath that header instead of sliding under it:
|
|
1271
|
+
|
|
1272
|
+
```vue
|
|
1273
|
+
<router-tab class="app-router-tab" />
|
|
1274
|
+
```
|
|
1275
|
+
|
|
1276
|
+
```css
|
|
1277
|
+
.app-router-tab {
|
|
1278
|
+
--router-tab-sticky-top: var(--v-layout-top, 0px);
|
|
1279
|
+
}
|
|
1280
|
+
```
|
|
1281
|
+
|
|
1275
1282
|
### Custom Styles Example
|
|
1276
1283
|
|
|
1277
1284
|
```css
|
package/dist/vue3-router-tab.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--router-tab-header-height: 42px;--router-tab-padding: 12px;--router-tab-font-size: 14px;--router-tab-transition: all .3s ease;--router-tab-primary: #3b82f6;--router-tab-background: #ffffff;--router-tab-text: #0f172a;--router-tab-border: #e2e8f0;--router-tab-header-bg: #ffffff;--router-tab-active-background: #3b82f6;--router-tab-active-text: #ffffff;--router-tab-active-border: #3b82f6;--router-tab-icon-color: #475569;--router-tab-button-background: #f8fafc;--router-tab-button-color: #0f172a;--router-tab-active-button-background: #3b82f6;--router-tab-active-button-color: #ffffff}:root[data-theme=dark]{--router-tab-background: #1e293b;--router-tab-text: #f1f5f9;--router-tab-border: #334155;--router-tab-header-bg: #1e293b;--router-tab-icon-color: #cbd5e1;--router-tab-button-background: #1f2937;--router-tab-button-color: #f8fafc;--router-tab-active-button-background: #38bdf8;--router-tab-active-button-color: #0f172a}.router-tab{display:flex;flex-direction:column;min-height:300px;background-color:var(--router-tab-background);color:var(--router-tab-text)}.router-tab__header{position:
|
|
1
|
+
:root{--router-tab-header-height: 42px;--router-tab-sticky-top: 0px;--router-tab-header-z-index: 10;--router-tab-padding: 12px;--router-tab-font-size: 14px;--router-tab-transition: all .3s ease;--router-tab-primary: #3b82f6;--router-tab-background: #ffffff;--router-tab-text: #0f172a;--router-tab-border: #e2e8f0;--router-tab-header-bg: #ffffff;--router-tab-active-background: #3b82f6;--router-tab-active-text: #ffffff;--router-tab-active-border: #3b82f6;--router-tab-icon-color: #475569;--router-tab-button-background: #f8fafc;--router-tab-button-color: #0f172a;--router-tab-active-button-background: #3b82f6;--router-tab-active-button-color: #ffffff}:root[data-theme=dark]{--router-tab-background: #1e293b;--router-tab-text: #f1f5f9;--router-tab-border: #334155;--router-tab-header-bg: #1e293b;--router-tab-icon-color: #cbd5e1;--router-tab-button-background: #1f2937;--router-tab-button-color: #f8fafc;--router-tab-active-button-background: #38bdf8;--router-tab-active-button-color: #0f172a}.router-tab{display:flex;flex-direction:column;min-height:300px;background-color:var(--router-tab-background);color:var(--router-tab-text)}.router-tab__header{position:sticky;top:var(--router-tab-sticky-top);z-index:var(--router-tab-header-z-index);display:flex;flex:none;align-items:center;box-sizing:border-box;height:var(--router-tab-header-height);background-color:var(--router-tab-header-bg);border-bottom:1px solid var(--router-tab-border);transition:all .2s ease}.router-tab__header.is-not-sticky{position:relative;top:auto}.router-tab__scroll{position:relative;flex:1 1 0px;height:100%;overflow-x:auto;overflow-y:hidden;scroll-behavior:smooth;scrollbar-width:thin;scrollbar-color:var(--router-tab-border) transparent}.router-tab__scroll::-webkit-scrollbar{height:4px}.router-tab__scroll::-webkit-scrollbar-track{background:transparent}.router-tab__scroll::-webkit-scrollbar-thumb{background:var(--router-tab-border);border-radius:2px}.router-tab__scroll::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb,var(--router-tab-primary) 50%,transparent)}.router-tab__scroll-container{width:100%;height:100%;overflow:hidden}.router-tab__scroll-container.is-mobile{overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.router-tab__scroll-container.is-mobile::-webkit-scrollbar{display:none}.router-tab__nav{position:relative;display:inline-flex;flex-wrap:nowrap;height:100%;margin:0;padding:0;list-style:none}.router-tab__item{position:relative;display:flex;flex:none;align-items:center;gap:.5rem;padding:0 var(--router-tab-padding);color:var(--router-tab-text);font-size:var(--router-tab-font-size);background-color:transparent;border:1px solid var(--router-tab-border);cursor:pointer;-webkit-user-select:none;user-select:none;transition:var(--router-tab-transition);will-change:background-color,color}.router-tab__item:first-child{border-left:1px solid var(--router-tab-border)}.router-tab__item:hover{background-color:color-mix(in srgb,var(--router-tab-primary) 4%,transparent);color:var(--router-tab-primary)}.router-tab__item.is-active{background-color:var(--router-tab-active-background);color:var(--router-tab-active-text);font-weight:510}.router-tab__item.is-active:after{content:"";position:absolute;bottom:-1px;left:0;right:0;height:2px;background-color:var(--router-tab-active-border)}.router-tab__item.is-active+.router-tab__item{border-left-color:transparent}.router-tab__item-icon{flex-shrink:0;font-size:14px;color:var(--router-tab-icon-color)}.router-tab__item.is-active .router-tab__item-icon{color:var(--router-tab-active-text)}.router-tab__item:hover .router-tab__item-icon{color:currentColor}.router-tab__item-title{min-width:auto;max-width:180px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.router-tab__item-close{position:relative;display:block;width:0;height:12px;flex-shrink:0;overflow:hidden;border:none;border-radius:50%;background:var(--router-tab-button-background);color:var(--router-tab-button-color);cursor:pointer;transition:var(--router-tab-transition)}.router-tab__item-close:before,.router-tab__item-close:after{position:absolute;top:50%;left:50%;display:block;width:8px;height:1px;margin-left:-4px;background-color:currentColor;transition:background-color .2s ease-in-out;content:""}.router-tab__item-close:before{transform:translateY(-50%) rotate(-45deg)}.router-tab__item-close:after{transform:translateY(-50%) rotate(45deg)}.router-tab__item-close:hover{background:var(--router-tab-active-button-background);color:var(--router-tab-active-button-color)}.router-tab__item:hover .router-tab__item-close,.router-tab__item.is-active .router-tab__item-close{width:12px;margin-left:4px}.router-tab__item.is-dragging{opacity:.6;cursor:grabbing}.router-tab__item.is-drag-over{background-color:color-mix(in srgb,var(--router-tab-primary) 12%,transparent)}.router-tab__container{position:relative;flex:1 1 auto;background-color:var(--router-tab-background);overflow:hidden}.router-tab__container{padding:10px;border:1px solid var(--router-tab-border);transition:var(--router-tab-transition);will-change:background-color,border-color}.router-tab__slot-start,.router-tab__slot-end{display:none;align-items:center;gap:.25rem;padding:0 .5rem;height:100%}.router-tab__slot-start.has-content{display:flex;border-right:1px solid var(--router-tab-border)}.router-tab__slot-end.has-content{display:flex;border-left:1px solid var(--router-tab-border)}.router-tab__contextmenu{position:fixed;z-index:1000;min-width:160px;padding:6px 0;font-size:var(--router-tab-font-size);background:var(--router-tab-background);border:1px solid var(--router-tab-border);border-radius:8px;box-shadow:0 2px 8px #0000001a}.router-tab__contextmenu-item{display:block;width:100%;padding:5px 12px;line-height:32px;text-align:left;text-decoration:none;background:var(--router-tab-button-background);border:none;color:var(--router-tab-button-color);cursor:pointer;font:inherit;border-radius:4px;transition:all .2s ease-in-out;outline:none}.router-tab__contextmenu-item[aria-disabled=true]{color:color-mix(in srgb,var(--router-tab-text) 40%);pointer-events:none;cursor:not-allowed}.router-tab__contextmenu-item:focus,.router-tab__contextmenu-item:focus-visible{outline:none}.router-tab__contextmenu-item:hover:not([aria-disabled=true]),.router-tab__contextmenu-item:focus-visible,.router-tab__contextmenu-item.is-focused{background:var(--router-tab-active-button-background);color:var(--router-tab-active-button-color)}.router-tab-zoom-enter-active,.router-tab-zoom-leave-active{transition:transform all .2s ease,opacity all .2s ease}.router-tab-zoom-enter-from,.router-tab-zoom-leave-to{transform:scale(.9);opacity:0}.router-tab-zoom-enter-to,.router-tab-zoom-leave-from{transform:scale(1);opacity:1}.router-tab-zoom-move{transition:transform all .2s ease}.router-tab-swap-enter-active,.router-tab-swap-leave-active{position:relative;transition:opacity .3s ease,transform .3s ease;will-change:opacity,transform}.router-tab-swap-enter-from{opacity:0;transform:translateY(20px)}.router-tab-swap-enter-to,.router-tab-swap-leave-from{opacity:1;transform:translateY(0)}.router-tab-swap-leave-to{opacity:0;transform:translateY(-15px)}.router-tab-slide-enter-active,.router-tab-slide-leave-active{position:relative;transition:opacity .3s ease,transform .3s ease;will-change:opacity,transform}.router-tab-slide-enter-from{opacity:0;transform:translate(80px)}.router-tab-slide-enter-to,.router-tab-slide-leave-from{opacity:1;transform:translate(0)}.router-tab-slide-leave-to{opacity:0;transform:translate(-80px)}.router-tab-fade-enter-active{transition:opacity .4s ease-in;will-change:opacity}.router-tab-fade-leave-active{transition:opacity .3s ease-out;will-change:opacity}.router-tab-fade-enter-from{opacity:0}.router-tab-fade-enter-to,.router-tab-fade-leave-from{opacity:1}.router-tab-fade-leave-to{opacity:0}.router-tab-scale-enter-active,.router-tab-scale-leave-active{position:relative;transition:opacity .35s ease,transform .35s cubic-bezier(.34,1.56,.64,1);will-change:opacity,transform}.router-tab-scale-enter-from{opacity:0;transform:scale(.7)}.router-tab-scale-enter-to,.router-tab-scale-leave-from{opacity:1;transform:scale(1)}.router-tab-scale-leave-to{opacity:0;transform:scale(1.2)}.router-tab-flip-enter-active,.router-tab-flip-leave-active{position:relative;transition:opacity .35s ease,transform .35s ease;transform-style:preserve-3d;will-change:opacity,transform}.router-tab-flip-enter-from{opacity:0;transform:rotateX(-90deg)}.router-tab-flip-enter-to,.router-tab-flip-leave-from{opacity:1;transform:rotateX(0)}.router-tab-flip-leave-to{opacity:0;transform:rotateX(90deg)}.router-tab-rotate-enter-active,.router-tab-rotate-leave-active{position:relative;transition:opacity .2s ease,transform .2s ease;will-change:opacity,transform}.router-tab-rotate-enter-from{opacity:0;transform:rotate(-8deg) scale(.85)}.router-tab-rotate-enter-to,.router-tab-rotate-leave-from{opacity:1;transform:rotate(0) scale(1)}.router-tab-rotate-leave-to{opacity:0;transform:rotate(8deg) scale(.85)}.router-tab-bounce-enter-active{animation:router-tab-bounce-in .5s cubic-bezier(.68,-.55,.265,1.55)}.router-tab-bounce-leave-active{animation:router-tab-bounce-out .3s ease-in}@keyframes router-tab-bounce-in{0%{opacity:0;transform:scale(.3) translateY(-80px)}50%{opacity:1;transform:scale(1.1)}70%{transform:scale(.9)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes router-tab-bounce-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.7) translateY(30px)}}
|