vue3-router-tab 1.2.7 → 1.2.8

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.
@@ -1 +1 @@
1
- (function(w,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("vue"),require("vue-router")):typeof define=="function"&&define.amd?define(["exports","vue","vue-router"],n):(w=typeof globalThis<"u"?globalThis:w||self,n(w["vue3-router-tab"]={},w.Vue,w.VueRouter))})(this,(function(w,n,Ce){"use strict";function Re(e={}){return{initialTabs:e.initialTabs??[],keepAlive:e.keepAlive??!0,maxAlive:e.maxAlive??0,keepLastTab:e.keepLastTab??!0,appendPosition:e.appendPosition??"last",defaultRoute:e.defaultRoute??"/"}}function M(e,a){const o=e.resolve(a);if(!o||!o.matched.length)throw new Error(`[RouterTabs] Unable to resolve route: ${String(a)}`);return o}const Ee={path:e=>e.path,fullpath:e=>e.fullPath,fullname:e=>e.fullPath,full:e=>e.fullPath,name:e=>e.name?String(e.name):e.fullPath};function $(e){const a=e.meta?.key;if(typeof a=="function"){const o=a(e);if(typeof o=="string"&&o.length)return o}else if(typeof a=="string"&&a.length){const o=Ee[a.toLowerCase()];return o?o(e):a}return e.fullPath}function G(e,a){const o=e.meta?.keepAlive;return typeof o=="boolean"?o:a}function X(e,a){const o=e.meta?.reuse;return typeof o=="boolean"?o:a}function se(e){const a=e.meta??{},o={};return"title"in a&&(o.title=a.title),"tips"in a&&(o.tips=a.tips),"icon"in a&&(o.icon=a.icon),"closable"in a&&(o.closable=a.closable),"tabClass"in a&&(o.tabClass=a.tabClass),"target"in a&&(o.target=a.target),"href"in a&&(o.href=a.href),o}function _(e,a,o){const s=se(e);return{id:$(e),to:e.fullPath,fullPath:e.fullPath,matched:e,alive:G(e,o),reusable:X(e,!1),closable:s.closable??!0,...s,...a}}function Q(e,a,o,s){if(!e.find(p=>p.id===a.id)){if(o==="next"&&s){const p=e.findIndex(k=>k.id===s);if(p!==-1){e.splice(p+1,0,a);return}}e.push(a)}}function ce(e,a,o){if(!a||a<=0)return;const s=e.filter(r=>r.alive);for(;s.length>a;){const r=s.shift();if(!r||r.id===o)continue;const p=e.findIndex(k=>k.id===r.id);p>-1&&(e[p].alive=!1)}}function Pe(e){return{to:e.to,title:e.title,tips:e.tips,icon:e.icon,tabClass:e.tabClass,closable:e.closable}}function Be(e){const a={};return"title"in e&&(a.title=e.title),"tips"in e&&(a.tips=e.tips),"icon"in e&&(a.icon=e.icon),"tabClass"in e&&(a.tabClass=e.tabClass),"closable"in e&&(a.closable=e.closable),a}function Ae(e,a={}){const o=Re(a),s=n.reactive([]),r=n.ref(null),p=n.shallowRef(),k=n.ref(null),c=n.computed(()=>s.filter(l=>l.alive).map(l=>l.id));let d=!1;function g(l){const b=typeof l.matched=="object"?l:M(e,l);return{key:$(b),fullPath:b.fullPath,alive:G(b,o.keepAlive),reusable:X(b,!1),matched:b}}function R(l){const b=$(l);let m=s.find(v=>v.id===b);return m?(m.fullPath=l.fullPath,m.to=l.fullPath,m.matched=l,m.alive=G(l,o.keepAlive),m.reusable=X(l,m.reusable),Object.assign(m,se(l)),m):(m=_(l,{},o.keepAlive),Q(s,m,o.appendPosition,r.value),ce(s,o.maxAlive,r.value),m)}async function I(l,b=!1,m=!0){const v=M(e,l),E=$(v),P=r.value===E;m==="sameTab"&&(m=P),m&&await K(E,!0),await e[b?"replace":"push"](v),P&&await T()}function x(l){const b=s.findIndex(A=>A.id===l);if(b===-1)return o.defaultRoute;const m=s[b+1],v=s[b-1],E=s.find(A=>A.id!==l),P=m||v||E;return P?P.to:o.defaultRoute}async function j(l=r.value,b={}){if(!l)return;if(!b.force&&o.keepLastTab&&s.length===1)throw new Error("[RouterTabs] Unable to close the final tab when keepLastTab is true.");const v=r.value===l&&b.redirect!==null,E=v?b.redirect??x(l):null;await O(l,{force:b.force}),b.redirect!==null&&v&&E&&await e.replace(E)}async function O(l,b={}){const m=s.findIndex(v=>v.id===l);m!==-1&&(s.splice(m,1),k.value===l&&(k.value=null),r.value===l&&(r.value=null,p.value=void 0))}async function K(l=r.value??void 0,b=!1){if(!l)return;const m=s.find(v=>v.id===l);m&&(o.keepAlive&&m.alive&&(m.alive=!1,await n.nextTick(),m.alive=!0,await n.nextTick()),k.value=l,await n.nextTick(),b||await n.nextTick(),k.value=null)}async function oe(l=!1){for(const b of s)await K(b.id,l)}async function ae(l=o.defaultRoute){s.splice(0,s.length),r.value=null,p.value=void 0;for(const b of o.initialTabs){const m=M(e,b.to),v=_(m,b,o.keepAlive);s.push(v)}await e.replace(l)}async function T(){const l=r.value;l&&await K(l,!0)}function W(l){return typeof l.matched=="object"?$(l):$(M(e,l))}function D(){const l=s.find(b=>b.id===r.value);return{tabs:s.map(Pe),active:l?l.to:null}}async function S(l){d=!0,s.splice(0,s.length),r.value=null,p.value=void 0;const b=l?.tabs??[];for(const v of b)try{const E=M(e,v.to),P=Be(v),A=_(E,P,o.keepAlive);Q(s,A,"last",null)}catch{}d=!1;const m=l?.active??b[b.length-1]?.to??o.defaultRoute;if(m)try{await e.replace(m)}catch{}}return n.watch(()=>e.currentRoute.value,l=>{if(d)return;const b=R(l);r.value=b.id,p.value=b,ce(s,o.maxAlive,r.value)},{immediate:!0}),o.initialTabs.length&&o.initialTabs.forEach(l=>{const b=M(e,l.to),m=_(b,l,o.keepAlive);Q(s,m,"last",null)}),{options:o,tabs:s,activeId:r,current:p,includeKeys:c,refreshingKey:k,openTab:I,closeTab:j,removeTab:O,refreshTab:K,refreshAll:oe,reset:ae,reload:T,getRouteKey:W,matchRoute:g,snapshot:D,hydrate:S}}function ue(e){return e?typeof e=="string"?{name:e}:e:{}}const V=Symbol("RouterTabsContext"),Y="router-tabs:snapshot";function Z(e={}){const{optional:a=!1}=e,o=n.inject(V,null);if(o)return o;const s=n.inject("$tabs",null);if(s)return s;const p=n.getCurrentInstance()?.appContext.config.globalProperties.$tabs;if(p)return p;if(!a)throw new Error("[RouterTabs] useRouterTabs must be used within <router-tab>.");return null}const Ie=864e5;function Se(e){if(typeof document>"u")return null;const a=`${encodeURIComponent(e)}=`,o=document.cookie?document.cookie.split("; "):[];for(const s of o)if(s.startsWith(a))return decodeURIComponent(s.slice(a.length));return null}function de(e,a,o){if(typeof document>"u")return;const{expiresInDays:s=7,path:r="/",domain:p,secure:k,sameSite:c="lax"}=o,d=[`${encodeURIComponent(e)}=${encodeURIComponent(a)}`];if(s!==1/0){const g=new Date(Date.now()+s*Ie).toUTCString();d.push(`Expires=${g}`)}r&&d.push(`Path=${r}`),p&&d.push(`Domain=${p}`),k&&d.push("Secure"),c&&d.push(`SameSite=${c.charAt(0).toUpperCase()}${c.slice(1)}`),document.cookie=d.join("; ")}function fe(e,a){if(typeof document>"u")return;const{path:o="/",domain:s}=a,r=[`${encodeURIComponent(e)}=`];r.push("Expires=Thu, 01 Jan 1970 00:00:01 GMT"),o&&r.push(`Path=${o}`),s&&r.push(`Domain=${s}`),document.cookie=r.join("; ")}const xe=e=>JSON.stringify(e??null),De=e=>{if(!e)return null;try{return JSON.parse(e)}catch{return null}};function ee(e={}){const{cookieKey:a=Y,serialize:o=xe,deserialize:s=De}=e,r=Z({optional:!0}),p=n.ref(!0),k=c=>{n.onMounted(async()=>{const d=s(Se(a));if(d&&d.tabs?.length)try{if(p.value=!0,await c.hydrate(d),d.active){await n.nextTick();const R=c.tabs.find(I=>I.to===d.active);R&&(c.activeId.value=R.id,c.current.value=R)}}finally{p.value=!1}else if(Object.prototype.hasOwnProperty.call(e,"fallbackRoute"))try{p.value=!0;const R=e.fallbackRoute??c.options.defaultRoute;await c.reset(R)}finally{p.value=!1}else p.value=!1;const g=c.snapshot();g.tabs.length?de(a,o(g),e):fe(a,e),p.value=!1}),n.watch(()=>({tabs:c.tabs.map(d=>({to:d.to,title:d.title,tips:d.tips,icon:d.icon,tabClass:d.tabClass,closable:d.closable})),active:c.activeId.value}),()=>{if(p.value)return;const d=c.snapshot();d.tabs.length?de(a,o(d),e):fe(a,e)},{deep:!0})};r?k(r):n.onMounted(()=>{const c=Z({optional:!0});c&&k(c)})}const Me=n.defineComponent({name:"RouterTab",components:{RouterView:Ce.RouterView},props:{tabs:{type:Array,default:()=>[]},keepAlive:{type:Boolean,default:!0},maxAlive:{type:Number,default:0},keepLastTab:{type:Boolean,default:!0},append:{type:String,default:"last"},defaultPage:{type:[String,Object],default:"/"},tabTransition:{type:[String,Object],default:"router-tab-zoom"},pageTransition:{type:[String,Object],default:()=>({name:"router-tab-swap",mode:"out-in"})},contextmenu:{type:[Boolean,Array],default:!0},cookieKey:{type:String,default:Y},persistence:{type:Object,default:null},sortable:{type:Boolean,default:!0}},emits:["tab-sort","tab-sorted"],setup(e,{emit:a}){const o=n.getCurrentInstance();if(!o)throw new Error("[RouterTab] component must be used within a Vue application context.");const s=o.appContext.app.config.globalProperties.$router;if(!s)throw new Error("[RouterTab] Vue Router is required. Make sure to call app.use(router) before RouterTab.");const r=Ae(s,{initialTabs:e.tabs,keepAlive:e.keepAlive,maxAlive:e.maxAlive,keepLastTab:e.keepLastTab,appendPosition:e.append,defaultRoute:e.defaultPage});n.provide(V,r),o.appContext.config.globalProperties.$tabs=r;const p=n.computed(()=>!!o?.slots?.default),k=n.computed(()=>!!o?.slots?.start),c=n.computed(()=>!!o?.slots?.end),d=n.ref(0),g=n.computed(()=>{d.value;const t={};return r.tabs.forEach(i=>{const f=typeof i.title=="string"?i.title:String(i.title||ie(i));t[i.id]=f}),t});function R(){d.value++}const I=new Map,x=new Map;function j(t,i){if(!i||I.has(t))return;I.set(t,i);const f=r.tabs.find(y=>r.getRouteKey(y.to)===t);if(!f)return;const h=[];if(i.routeTabTitle!==void 0){const y=n.watch(()=>{const u=i.routeTabTitle;return u&&typeof u=="object"&&"value"in u?u.value:u},u=>{if(u!=null){const L=String(u);f.title=L,R()}},{immediate:!0});h.push(y)}if(i.routeTabIcon!==void 0){const y=n.watch(()=>{const u=i.routeTabIcon;return u&&typeof u=="object"&&"value"in u?u.value:u},u=>{u!=null&&(f.icon=String(u),R())},{immediate:!0});h.push(y)}if(i.routeTabClosable!==void 0){const y=n.watch(()=>{const u=i.routeTabClosable;return u&&typeof u=="object"&&"value"in u?u.value:u},u=>{u!=null&&(f.closable=!!u,R())},{immediate:!0});h.push(y)}if(i.routeTabMeta!==void 0){const y=n.watch(()=>{const u=i.routeTabMeta;return u&&typeof u=="object"&&"value"in u?u.value:u},u=>{u&&typeof u=="object"&&(Object.assign(f,u),R())},{immediate:!0,deep:!0});h.push(y)}x.set(t,h)}function O(t){const i=x.get(t);i&&(i.forEach(f=>f()),x.delete(t)),I.delete(t)}function K(t,i){t?t.routeTabTitle!==void 0||t.routeTabIcon!==void 0||t.routeTabClosable!==void 0?j(i,t):t.$&&(t.$.routeTabTitle!==void 0||t.$.routeTabIcon!==void 0||t.$.routeTabClosable!==void 0)&&j(i,t.$):t===null&&O(i)}if(e.cookieKey!==null||e.persistence){const t={...e.persistence??{}};e.cookieKey!==null?t.cookieKey=e.cookieKey||Y:t.cookieKey||(t.cookieKey=Y),ee(t)}const oe=n.computed(()=>ue(e.tabTransition)),ae=n.computed(()=>ue(e.pageTransition)),T=n.reactive({visible:!1,target:null,position:{x:0,y:0}}),W=n.ref(null),D=n.ref([]),S=n.ref(-1),l=n.reactive({dragging:!1,dragIndex:-1,dropIndex:-1,dragTab:null}),b=["refresh","refreshAll","close","closeLefts","closeRights","closeOthers"];function m(t){return r.tabs.findIndex(i=>i.id===t)}function v(t){const i=m(t.id);return i>0?r.tabs.slice(0,i):[]}function E(t){const i=m(t.id);return i>-1?r.tabs.slice(i+1):[]}function P(t){return r.tabs.filter(i=>i.id!==t.id)}async function A(t,i){const f=t.filter(h=>h.closable!==!1);if(f.length){for(const h of f)r.activeId.value===h.id?await r.closeTab(h.id,{redirect:i.to,force:!0}):await r.removeTab(h.id,{force:!0});r.activeId.value!==i.id&&await r.openTab(i.to,!0,!1)}}const et={refresh:{label:"Refresh",handler:async({target:t})=>{await r.refreshTab(t.id,!0)}},refreshAll:{label:"Refresh All",handler:async()=>{await r.refreshAll(!0)}},close:{label:"Close",handler:async({target:t})=>{await r.closeTab(t.id)},enable:({target:t})=>re(t)},closeLefts:{label:"Close to the Left",handler:async({target:t})=>{await A(v(t),t)},enable:({target:t})=>v(t).some(i=>i.closable!==!1)},closeRights:{label:"Close to the Right",handler:async({target:t})=>{await A(E(t),t)},enable:({target:t})=>E(t).some(i=>i.closable!==!1)},closeOthers:{label:"Close Others",handler:async({target:t})=>{await A(P(t),t)},enable:({target:t})=>P(t).some(i=>i.closable!==!1)}};function B(){T.visible=!1,T.target=null,S.value=-1,D.value=[]}function tt(t,i){e.contextmenu&&(T.visible=!0,T.target=t,T.position.x=i.clientX,T.position.y=i.clientY,document.addEventListener("click",B,{once:!0}),n.nextTick(()=>{ot()}))}function nt(t,i){const f=typeof t=="string"?{id:t}:t,h=et[f.id],y=f.label??h?.label??String(f.id),u=f.visible??h?.visible??!0;if(!(typeof u=="function"?u(i):u!==!1))return null;const le=f.enable??h?.enable??!0,yt=typeof le=="function"?le(i):le!==!1,we=f.handler??h?.handler;if(!we)return null;const Tt=async()=>{await Promise.resolve(we(i))};return{id:String(f.id),label:y,disabled:!yt,action:Tt}}const U=n.computed(()=>{if(!T.visible||!T.target||e.contextmenu===!1)return[];const t=Array.isArray(e.contextmenu)?e.contextmenu:b,i={target:T.target,controller:r};return t.map(f=>nt(f,i)).filter(f=>!!f)});function ot(){const t=W.value;if(!t)return;const i=8,{innerWidth:f,innerHeight:h}=window,y=t.getBoundingClientRect();let u=T.position.x,L=T.position.y;y.right>f-i&&(u=Math.max(i,f-y.width-i)),y.bottom>h-i&&(L=Math.max(i,h-y.height-i)),(u!==T.position.x||L!==T.position.y)&&(T.position.x=u,T.position.y=L)}function at(t,i){D.value[i]=t??null}function it(t){if(t<0)return;D.value[t]?.focus({preventScroll:!0})}function q(t,i,f=U.value){if(!f.length)return-1;const h=f.length;let y=t;for(let u=0;u<h;u++)if(y=(y+i+h)%h,!f[y].disabled)return y;return-1}function z(t){S.value=t,!(t<0)&&n.nextTick(()=>it(t))}function Te(t){const i=q(S.value,t);i!==-1&&z(i)}function rt(t){if(!T.visible)return;const i=t.key,f=U.value;if(!f.length)return;if(i==="Tab"){B();return}if(["ArrowDown","ArrowUp","ArrowRight","ArrowLeft","Home","End","Enter"," ","Spacebar","Escape"].includes(i))switch(t.preventDefault(),i){case"ArrowDown":case"ArrowRight":Te(1);break;case"ArrowUp":case"ArrowLeft":Te(-1);break;case"Home":z(q(-1,1));break;case"End":z(q(f.length,-1));break;case"Enter":case" ":case"Spacebar":{const y=S.value;if(y>-1){const u=f[y];u.disabled||ke(u)}break}case"Escape":B();break}}async function ke(t){t.disabled||(B(),await t.action())}function ie(t){return typeof t.title=="string"&&t.title.trim()?t.title:Array.isArray(t.title)&&t.title.length&&String(t.title[0]).trim()?String(t.title[0]):"Untitled"}function lt(t){return g.value[t.id]||ie(t)}function re(t){return!(t.closable===!1||r.options.keepLastTab&&r.tabs.length<=1)}async function st(t){await r.closeTab(t.id)}function ct(t){if(t.href&&typeof window<"u"){t.target&&t.target!=="_self"?window.open(t.href,t.target):window.location.assign(t.href);return}r.activeId.value!==t.id&&r.openTab(t.to,!1)}function ut(t){return["router-tab__item",{"is-active":r.activeId.value===t.id,"is-closable":re(t),"is-dragging":l.dragging&&l.dragTab?.id===t.id,"is-drag-over":l.dropIndex===m(t.id)},t.tabClass]}function dt(t){return r.refreshingKey.value===r.getRouteKey(t)}function ft(t,i,f){e.sortable&&(l.dragging=!0,l.dragIndex=i,l.dragTab=t,f.dataTransfer&&(f.dataTransfer.effectAllowed="move",f.dataTransfer.setData("text/plain",t.id)),a("tab-sort",{tab:t,index:i}))}function bt(t,i){!e.sortable||!l.dragging||(i.preventDefault(),i.dataTransfer&&(i.dataTransfer.dropEffect="move"))}function mt(t){!e.sortable||!l.dragging||(l.dropIndex=t)}function pt(){!e.sortable||l.dragging}function gt(t,i){if(!(!e.sortable||!l.dragging)){if(i.preventDefault(),l.dragIndex!==-1&&l.dragIndex!==t){const f=r.tabs.splice(l.dragIndex,1)[0];r.tabs.splice(t,0,f),a("tab-sorted",{tab:f,fromIndex:l.dragIndex,toIndex:t})}ve()}}function ve(){l.dragging=!1,l.dragIndex=-1,l.dropIndex=-1,l.dragTab=null}n.onMounted(()=>{document.addEventListener("keydown",B)}),n.onBeforeUnmount(()=>{document.removeEventListener("keydown",B),o.appContext.config.globalProperties.$tabs=null,x.forEach(t=>{t.forEach(i=>i())}),x.clear(),I.clear()}),n.watch(()=>e.keepAlive,t=>{r.options.keepAlive=t}),n.watch(()=>r.activeId.value,()=>B()),n.watch(()=>e.contextmenu,t=>{t||B()}),n.watch(()=>U.value.length,t=>{T.visible&&t===0&&B()}),n.watch(U,t=>{if(D.value=new Array(t.length).fill(null),!T.visible)return;const i=q(-1,1,t);z(i)}),n.watch(()=>T.visible,t=>{t||(S.value=-1,D.value=[])});const ht=r.includeKeys;return{controller:r,tabs:r.tabs,includeKeys:ht,tabTransitionProps:oe,pageTransitionProps:ae,buildTabClass:ut,activate:ct,close:st,context:T,menuItems:U,handleMenuAction:ke,showContextMenu:tt,hideContextMenu:B,getTabTitle:ie,isClosable:re,isRefreshing:dt,hasCustomSlot:p,hasStartSlot:k,hasEndSlot:c,onDragStart:ft,onDragOver:bt,onDragEnter:mt,onDragLeave:pt,onDrop:gt,onDragEnd:ve,setupComponentWatching:j,cleanupComponentWatching:O,handleComponentRef:K,getReactiveTabTitle:lt,triggerTabUpdate:R,menuRef:W,highlightedIndex:S,setMenuItemRef:at,onMenuKeydown:rt,highlightMenuIndex:z}}}),$e=(e,a)=>{const o=e.__vccOpts||e;for(const[s,r]of a)o[s]=r;return o},Ke={class:"router-tab"},Le={class:"router-tab__header"},Ve={class:"router-tab__scroll"},Ne=["data-title","draggable","onClick","onAuxclick","onContextmenu","onDragstart","onDragover","onDragenter","onDrop"],je=["title"],Oe=["onClick"],Ue={class:"router-tab__container"},ze=["aria-disabled","disabled","tabindex","onMouseenter","onClick"];function _e(e,a,o,s,r,p){const k=n.resolveComponent("RouterView");return n.openBlock(),n.createElementBlock("div",Ke,[n.createElementVNode("header",Le,[n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-start",{"has-content":e.hasStartSlot}])},[n.renderSlot(e.$slots,"start")],2),n.createElementVNode("div",Ve,[n.createVNode(n.TransitionGroup,n.mergeProps({tag:"ul",class:"router-tab__nav"},e.tabTransitionProps),{default:n.withCtx(()=>[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(e.tabs,(c,d)=>(n.openBlock(),n.createElementBlock("li",{key:c.id,class:n.normalizeClass(e.buildTabClass(c)),"data-title":e.getTabTitle(c),draggable:e.sortable,onClick:g=>e.activate(c),onAuxclick:n.withModifiers(g=>e.close(c),["middle","prevent"]),onContextmenu:n.withModifiers(g=>e.showContextMenu(c,g),["prevent"]),onDragstart:g=>e.onDragStart(c,d,g),onDragover:g=>e.onDragOver(d,g),onDragenter:g=>e.onDragEnter(d),onDragleave:a[0]||(a[0]=(...g)=>e.onDragLeave&&e.onDragLeave(...g)),onDrop:g=>e.onDrop(d,g),onDragend:a[1]||(a[1]=(...g)=>e.onDragEnd&&e.onDragEnd(...g))},[c.icon?(n.openBlock(),n.createElementBlock("i",{key:0,class:n.normalizeClass(["router-tab__item-icon",c.icon])},null,2)):n.createCommentVNode("",!0),n.createElementVNode("span",{class:"router-tab__item-title",title:e.getReactiveTabTitle(c)},n.toDisplayString(e.getReactiveTabTitle(c)),9,je),e.isClosable(c)?(n.openBlock(),n.createElementBlock("a",{key:1,class:"router-tab__item-close",onClick:n.withModifiers(g=>e.close(c),["stop"])},null,8,Oe)):n.createCommentVNode("",!0)],42,Ne))),128))]),_:1},16)]),n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-end",{"has-content":e.hasEndSlot}])},[n.renderSlot(e.$slots,"end")],2)]),n.createElementVNode("div",Ue,[n.createVNode(k,null,{default:n.withCtx(c=>[e.hasCustomSlot?n.renderSlot(e.$slots,"default",n.normalizeProps(n.mergeProps({key:0},{...c,controller:e.controller,pageRef:d=>e.handleComponentRef(d,e.controller.getRouteKey(c.route))}))):(n.openBlock(),n.createElementBlock(n.Fragment,{key:1},[n.createVNode(n.Transition,n.mergeProps(e.pageTransitionProps,{appear:""}),{default:n.withCtx(()=>[e.controller.options.keepAlive?(n.openBlock(),n.createBlock(n.KeepAlive,{key:0,include:e.includeKeys,max:e.controller.options.maxAlive||void 0},[e.isRefreshing(c.route)?n.createCommentVNode("",!0):(n.openBlock(),n.createBlock(n.resolveDynamicComponent(c.Component),{key:e.controller.getRouteKey(c.route),ref:d=>e.handleComponentRef(d,e.controller.getRouteKey(c.route)),class:"router-tab-page"}))],1032,["include","max"])):n.createCommentVNode("",!0)]),_:2},1040),n.createVNode(n.Transition,n.mergeProps(e.pageTransitionProps,{appear:""}),{default:n.withCtx(()=>[!e.controller.options.keepAlive||e.isRefreshing(c.route)?(n.openBlock(),n.createBlock(n.resolveDynamicComponent(c.Component),{key:e.controller.getRouteKey(c.route)+(e.isRefreshing(c.route)?"-refresh":""),ref:d=>e.handleComponentRef(d,e.controller.getRouteKey(c.route)),class:"router-tab-page"})):n.createCommentVNode("",!0)]),_:2},1040)],64))]),_:3})]),e.context.visible&&e.context.target?(n.openBlock(),n.createElementBlock("div",{key:0,ref:"menuRef",class:"router-tab__contextmenu",role:"menu",onKeydown:a[2]||(a[2]=(...c)=>e.onMenuKeydown&&e.onMenuKeydown(...c)),style:n.normalizeStyle({left:e.context.position.x+"px",top:e.context.position.y+"px"})},[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(e.menuItems,(c,d)=>(n.openBlock(),n.createElementBlock("a",{key:c.id,role:"menuitem",class:n.normalizeClass(["router-tab__contextmenu-item",{"is-focused":d===e.highlightedIndex}]),"aria-disabled":c.disabled,disabled:c.disabled,tabindex:c.disabled?-1:d===e.highlightedIndex?0:-1,ref_for:!0,ref:g=>e.setMenuItemRef(g,d),onMouseenter:g=>!c.disabled&&e.highlightMenuIndex(d),onClick:g=>e.handleMenuAction(c)},n.toDisplayString(c.label),43,ze))),128))],36)):n.createCommentVNode("",!0)])}const te=$e(Me,[["render",_e]]),Ye={class:"router-tabs","aria-hidden":"true"},F=n.defineComponent({name:"RouterTabs",__name:"RouterTabs",props:{cookieKey:{},expiresInDays:{},path:{},domain:{},secure:{type:Boolean},sameSite:{},serialize:{type:Function},deserialize:{type:Function},fallbackRoute:{}},setup(e){return ee(e),(o,s)=>(n.openBlock(),n.createElementBlock("span",Ye))}}),be="tab-theme-style",me="tab-theme-primary-color",Fe="system",pe="(prefers-color-scheme: dark)";let N=null;const C={primary:"#034960",background:"#ffffff",text:"#1e293b",border:"#e2e8f0",activeBackground:"#034960",activeText:"#ffffff",activeBorder:"#034960",headerBackground:"#ffffff",buttonBackground:"#f8fafc",buttonColor:"#034960",activeButtonBackground:"#034960",activeButtonColor:"#ffffff",iconColor:"#475569"},He={primary:"#38bdf8",background:"#0f172a",text:"#f1f5f9",border:"#334155",activeBackground:"#1e293b",activeText:"#38bdf8",activeBorder:"#38bdf8",headerBackground:"#0c4a6e",buttonBackground:"#1e293b",buttonColor:"#f1f5f9",activeButtonBackground:"#38bdf8",activeButtonColor:"#0f172a",iconColor:"#cbd5e1"};function Je(e){if(typeof window>"u")return null;const a=window.localStorage.getItem(e);if(!a)return null;try{const o=JSON.parse(a);return o&&typeof o=="object"?o:null}catch{return null}}function ne(e){typeof document>"u"||(document.documentElement.style.setProperty("--router-tab-primary",e.primary??C.primary),document.documentElement.style.setProperty("--router-tab-header-bg",e.headerBackground??C.headerBackground),document.documentElement.style.setProperty("--router-tab-background",e.background??C.background),document.documentElement.style.setProperty("--router-tab-active-background",e.activeBackground??C.activeBackground),document.documentElement.style.setProperty("--router-tab-text",e.text??C.text),document.documentElement.style.setProperty("--router-tab-active-text",e.activeText??C.activeText),document.documentElement.style.setProperty("--router-tab-border",e.border??C.border),document.documentElement.style.setProperty("--router-tab-active-border",e.activeBorder??C.activeBorder),document.documentElement.style.setProperty("--router-tab-button-color",e.buttonColor??C.buttonColor),document.documentElement.style.setProperty("--router-tab-active-button-color",e.activeButtonColor??C.activeButtonColor),document.documentElement.style.setProperty("--router-tab-button-background",e.buttonBackground??C.buttonBackground),document.documentElement.style.setProperty("--router-tab-active-button-background",e.activeButtonBackground??C.activeButtonBackground),document.documentElement.style.setProperty("--router-tab-icon-color",e.iconColor??C.iconColor))}function ge(e){if(typeof document>"u")return;const a=document.documentElement,o=window.matchMedia(pe),s=()=>{a.dataset.theme=o.matches?"dark":"light"};N&&(o.removeEventListener("change",N),N=null),e==="system"?(s(),N=()=>s(),o.addEventListener("change",N)):a.dataset.theme=e}function he(e={}){if(typeof window>"u")return;const{styleKey:a=be,primaryKey:o=me,defaultStyle:s=Fe,defaultPrimary:r}=e,p=window.localStorage.getItem(a)??s;ge(p);const c=p==="dark"||p==="system"&&window.matchMedia(pe).matches?{...He}:{...C};r&&(c.primary=r);const d=Je(o);ne(d?{...c,...d}:c)}function We(e,a){if(typeof window>"u")return;const o=a?.styleKey??be;window.localStorage.setItem(o,e),ge(e)}function qe(e,a){if(typeof window>"u")return;const o=a?.primaryKey??me;window.localStorage.setItem(o,JSON.stringify(e)),ne(e)}function H(e,a){if(n.isRef(e)){const s=!n.isReadonly(e);return{value:e,update:s?r=>{e.value=r}:()=>{}}}if(typeof e=="function"){const s=e;return{value:n.computed(s),update:()=>{}}}const o=n.ref(e===void 0?a:e);return{value:o,update:s=>{o.value=s}}}function J(e={}){const a=H(e.title,"Untitled"),o=H(e.icon,""),s=H(e.closable,!0),r=H(e.meta,{});return{routeTabTitle:a.value,routeTabIcon:o.value,routeTabClosable:s.value,routeTabMeta:r.value,updateTitle:a.update,updateIcon:o.update,updateClosable:s.update,updateMeta:r.update}}function Ge(e,a="Page"){return J({title:n.computed(()=>e.value?"Loading...":a),icon:n.computed(()=>e.value?"mdi-loading mdi-spin":"mdi-page"),closable:n.computed(()=>!e.value)})}function Xe(e,a="Page",o="mdi-page"){return J({title:n.computed(()=>e.value>0?`${a} (${e.value})`:a),icon:n.computed(()=>e.value>0?"mdi-bell-badge":o)})}function Qe(e,a="Page"){const o={normal:{suffix:"",icon:"mdi-page"},loading:{suffix:" - Loading",icon:"mdi-loading mdi-spin"},error:{suffix:" - Error",icon:"mdi-alert"},success:{suffix:" - Success",icon:"mdi-check-circle"}};return J({title:n.computed(()=>a+o[e.value].suffix),icon:n.computed(()=>o[e.value].icon),closable:n.computed(()=>e.value!=="loading")})}let ye=!1;const Ze={install(e,a){if(ye)return;ye=!0;const{initTheme:o=!0,themeOptions:s,componentName:r=te.name||"RouterTab",tabsComponentName:p=F.name||"RouterTabs"}=a??{};o&&he(s??{}),e.component(r,te),e.component(p,F),p.toLowerCase()!=="router-tabs"&&e.component("router-tabs",F),Object.defineProperty(e.config.globalProperties,"$tabs",{configurable:!0,enumerable:!1,get(){return e._context.provides[V]},set(k){k&&e.provide(V,k)}})}};w.RouterTab=te,w.RouterTabs=F,w.default=Ze,w.initRouterTabsTheme=he,w.routerTabsKey=V,w.setRouterTabsPrimary=qe,w.setRouterTabsTheme=We,w.useLoadingTab=Ge,w.useNotificationTab=Xe,w.useReactiveTab=J,w.useRouterTabs=Z,w.useRouterTabsPersistence=ee,w.useStatusTab=Qe,Object.defineProperties(w,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
1
+ (function(w,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("vue"),require("vue-router")):typeof define=="function"&&define.amd?define(["exports","vue","vue-router"],n):(w=typeof globalThis<"u"?globalThis:w||self,n(w["vue3-router-tab"]={},w.Vue,w.VueRouter))})(this,(function(w,n,Re){"use strict";function Ee(e={}){return{initialTabs:e.initialTabs??[],keepAlive:e.keepAlive??!0,maxAlive:e.maxAlive??0,keepLastTab:e.keepLastTab??!0,appendPosition:e.appendPosition??"last",defaultRoute:e.defaultRoute??"/"}}function D(e,o){const a=e.resolve(o);if(!a||!a.matched.length)throw new Error(`[RouterTabs] Unable to resolve route: ${String(o)}`);return a}const Pe={path:e=>e.path,fullpath:e=>e.fullPath,fullname:e=>e.fullPath,full:e=>e.fullPath,name:e=>e.name?String(e.name):e.fullPath};function $(e){const o=e.meta?.key;if(typeof o=="function"){const a=o(e);if(typeof a=="string"&&a.length)return a}else if(typeof o=="string"&&o.length){const a=Pe[o.toLowerCase()];return a?a(e):o}return e.fullPath}function G(e,o){const a=e.meta?.keepAlive;return typeof a=="boolean"?a:o}function X(e,o){const a=e.meta?.reuse;return typeof a=="boolean"?a:o}function se(e){const o=e.meta??{},a={};return"title"in o&&(a.title=o.title),"tips"in o&&(a.tips=o.tips),"icon"in o&&(a.icon=o.icon),"closable"in o&&(a.closable=o.closable),"tabClass"in o&&(a.tabClass=o.tabClass),"target"in o&&(a.target=o.target),"href"in o&&(a.href=o.href),a}function _(e,o,a){const s=se(e);return{id:$(e),to:e.fullPath,fullPath:e.fullPath,matched:e,alive:G(e,a),reusable:X(e,!1),closable:s.closable??!0,renderKey:typeof o.renderKey=="number"?o.renderKey:0,...s,...o}}function Q(e,o,a,s){if(!e.find(p=>p.id===o.id)){if(a==="next"&&s){const p=e.findIndex(v=>v.id===s);if(p!==-1){e.splice(p+1,0,o);return}}e.push(o)}}function ce(e,o,a){if(!o||o<=0)return;const s=e.filter(i=>i.alive);for(;s.length>o;){const i=s.shift();if(!i||i.id===a)continue;const p=e.findIndex(v=>v.id===i.id);p>-1&&(e[p].alive=!1)}}function Be(e){return{to:e.to,title:e.title,tips:e.tips,icon:e.icon,tabClass:e.tabClass,closable:e.closable,renderKey:e.renderKey}}function Ae(e){const o={};return"title"in e&&(o.title=e.title),"tips"in e&&(o.tips=e.tips),"icon"in e&&(o.icon=e.icon),"tabClass"in e&&(o.tabClass=e.tabClass),"closable"in e&&(o.closable=e.closable),"renderKey"in e&&typeof e.renderKey=="number"&&(o.renderKey=e.renderKey),o}function Ke(e,o={}){const a=Ee(o),s=n.reactive([]),i=n.ref(null),p=n.shallowRef(),v=n.ref(null),c=n.computed(()=>s.filter(l=>l.alive).map(l=>`${l.id}::${l.renderKey}`));let d=!1;function g(l){const b=typeof l.matched=="object"?l:D(e,l);return{key:$(b),fullPath:b.fullPath,alive:G(b,a.keepAlive),reusable:X(b,!1),matched:b}}function E(l){const b=$(l);let m=s.find(k=>k.id===b);return m?(m.fullPath=l.fullPath,m.to=l.fullPath,m.matched=l,m.alive=G(l,a.keepAlive),m.reusable=X(l,m.reusable),Object.assign(m,se(l)),m):(m=_(l,{},a.keepAlive),Q(s,m,a.appendPosition,i.value),ce(s,a.maxAlive,i.value),m)}async function K(l,b=!1,m=!0){const k=D(e,l),C=$(k),P=i.value===C;m==="sameTab"&&(m=P),m&&await M(C,!0),await e[b?"replace":"push"](k),P&&await T()}function S(l){const b=s.findIndex(A=>A.id===l);if(b===-1)return a.defaultRoute;const m=s[b+1],k=s[b-1],C=s.find(A=>A.id!==l),P=m||k||C;return P?P.to:a.defaultRoute}async function j(l=i.value,b={}){if(!l)return;if(!b.force&&a.keepLastTab&&s.length===1)throw new Error("[RouterTabs] Unable to close the final tab when keepLastTab is true.");const k=i.value===l&&b.redirect!==null,C=k?b.redirect??S(l):null;await O(l,{force:b.force}),b.redirect!==null&&k&&C&&await e.replace(C)}async function O(l,b={}){const m=s.findIndex(k=>k.id===l);m!==-1&&(s.splice(m,1),v.value===l&&(v.value=null),i.value===l&&(i.value=null,p.value=void 0))}async function M(l=i.value??void 0,b=!1){if(!l)return;const m=s.find(C=>C.id===l);if(!m)return;const k=a.keepAlive&&m.alive;k&&(m.alive=!1,await n.nextTick()),m.renderKey=(m.renderKey??0)+1,k&&(m.alive=!0,await n.nextTick()),v.value=l,await n.nextTick(),b||await n.nextTick(),v.value=null}async function oe(l=!1){for(const b of s)await M(b.id,l)}async function ae(l=a.defaultRoute){s.splice(0,s.length),i.value=null,p.value=void 0;for(const b of a.initialTabs){const m=D(e,b.to),k=_(m,b,a.keepAlive);s.push(k)}await e.replace(l)}async function T(){const l=i.value;l&&await M(l,!0)}function W(l){return typeof l.matched=="object"?$(l):$(D(e,l))}function x(){const l=s.find(b=>b.id===i.value);return{tabs:s.map(Be),active:l?l.to:null}}async function I(l){d=!0,s.splice(0,s.length),i.value=null,p.value=void 0;const b=l?.tabs??[];for(const k of b)try{const C=D(e,k.to),P=Ae(k),A=_(C,P,a.keepAlive);Q(s,A,"last",null)}catch{}d=!1;const m=l?.active??b[b.length-1]?.to??a.defaultRoute;if(m)try{await e.replace(m)}catch{}}return n.watch(()=>e.currentRoute.value,l=>{if(d)return;const b=E(l);i.value=b.id,p.value=b,ce(s,a.maxAlive,i.value)},{immediate:!0}),a.initialTabs.length&&a.initialTabs.forEach(l=>{const b=D(e,l.to),m=_(b,l,a.keepAlive);Q(s,m,"last",null)}),{options:a,tabs:s,activeId:i,current:p,includeKeys:c,refreshingKey:v,openTab:K,closeTab:j,removeTab:O,refreshTab:M,refreshAll:oe,reset:ae,reload:T,getRouteKey:W,matchRoute:g,snapshot:x,hydrate:I}}function ue(e){return e?typeof e=="string"?{name:e}:e:{}}const V=Symbol("RouterTabsContext"),Y="router-tabs:snapshot";function Z(e={}){const{optional:o=!1}=e,a=n.inject(V,null);if(a)return a;const s=n.inject("$tabs",null);if(s)return s;const p=n.getCurrentInstance()?.appContext.config.globalProperties.$tabs;if(p)return p;if(!o)throw new Error("[RouterTabs] useRouterTabs must be used within <router-tab>.");return null}const Ie=864e5;function Se(e){if(typeof document>"u")return null;const o=`${encodeURIComponent(e)}=`,a=document.cookie?document.cookie.split("; "):[];for(const s of a)if(s.startsWith(o))return decodeURIComponent(s.slice(o.length));return null}function de(e,o,a){if(typeof document>"u")return;const{expiresInDays:s=7,path:i="/",domain:p,secure:v,sameSite:c="lax"}=a,d=[`${encodeURIComponent(e)}=${encodeURIComponent(o)}`];if(s!==1/0){const g=new Date(Date.now()+s*Ie).toUTCString();d.push(`Expires=${g}`)}i&&d.push(`Path=${i}`),p&&d.push(`Domain=${p}`),v&&d.push("Secure"),c&&d.push(`SameSite=${c.charAt(0).toUpperCase()}${c.slice(1)}`),document.cookie=d.join("; ")}function fe(e,o){if(typeof document>"u")return;const{path:a="/",domain:s}=o,i=[`${encodeURIComponent(e)}=`];i.push("Expires=Thu, 01 Jan 1970 00:00:01 GMT"),a&&i.push(`Path=${a}`),s&&i.push(`Domain=${s}`),document.cookie=i.join("; ")}const xe=e=>JSON.stringify(e??null),De=e=>{if(!e)return null;try{return JSON.parse(e)}catch{return null}};function ee(e={}){const{cookieKey:o=Y,serialize:a=xe,deserialize:s=De}=e,i=Z({optional:!0}),p=n.ref(!0),v=c=>{n.onMounted(async()=>{const d=s(Se(o));if(d&&d.tabs?.length)try{if(p.value=!0,await c.hydrate(d),d.active){await n.nextTick();const E=c.tabs.find(K=>K.to===d.active);E&&(c.activeId.value=E.id,c.current.value=E)}}finally{p.value=!1}else if(Object.prototype.hasOwnProperty.call(e,"fallbackRoute"))try{p.value=!0;const E=e.fallbackRoute??c.options.defaultRoute;await c.reset(E)}finally{p.value=!1}else p.value=!1;const g=c.snapshot();g.tabs.length?de(o,a(g),e):fe(o,e),p.value=!1}),n.watch(()=>({tabs:c.tabs.map(d=>({to:d.to,title:d.title,tips:d.tips,icon:d.icon,tabClass:d.tabClass,closable:d.closable,renderKey:d.renderKey})),active:c.activeId.value}),()=>{if(p.value)return;const d=c.snapshot();d.tabs.length?de(o,a(d),e):fe(o,e)},{deep:!0})};i?v(i):n.onMounted(()=>{const c=Z({optional:!0});c&&v(c)})}const $e=n.defineComponent({name:"RouterTab",components:{RouterView:Re.RouterView},props:{tabs:{type:Array,default:()=>[]},keepAlive:{type:Boolean,default:!0},maxAlive:{type:Number,default:0},keepLastTab:{type:Boolean,default:!0},append:{type:String,default:"last"},defaultPage:{type:[String,Object],default:"/"},tabTransition:{type:[String,Object],default:"router-tab-zoom"},pageTransition:{type:[String,Object],default:()=>({name:"router-tab-swap",mode:"out-in"})},contextmenu:{type:[Boolean,Array],default:!0},cookieKey:{type:String,default:Y},persistence:{type:Object,default:null},sortable:{type:Boolean,default:!0}},emits:["tab-sort","tab-sorted"],setup(e,{emit:o}){const a=n.getCurrentInstance();if(!a)throw new Error("[RouterTab] component must be used within a Vue application context.");const s=a.appContext.app.config.globalProperties.$router;if(!s)throw new Error("[RouterTab] Vue Router is required. Make sure to call app.use(router) before RouterTab.");const i=Ke(s,{initialTabs:e.tabs,keepAlive:e.keepAlive,maxAlive:e.maxAlive,keepLastTab:e.keepLastTab,appendPosition:e.append,defaultRoute:e.defaultPage});n.provide(V,i),a.appContext.config.globalProperties.$tabs=i;const p=n.computed(()=>!!a?.slots?.default),v=n.computed(()=>!!a?.slots?.start),c=n.computed(()=>!!a?.slots?.end),d=n.ref(0),g=n.computed(()=>{d.value;const t={};return i.tabs.forEach(r=>{const f=typeof r.title=="string"?r.title:String(r.title||re(r));t[r.id]=f}),t});function E(){d.value++}const K=new Map,S=new Map;function j(t,r){if(!r||K.has(t))return;K.set(t,r);const f=i.tabs.find(y=>i.getRouteKey(y.to)===t);if(!f)return;const h=[];if(r.routeTabTitle!==void 0){const y=n.watch(()=>{const u=r.routeTabTitle;return u&&typeof u=="object"&&"value"in u?u.value:u},u=>{if(u!=null){const L=String(u);f.title=L,E()}},{immediate:!0});h.push(y)}if(r.routeTabIcon!==void 0){const y=n.watch(()=>{const u=r.routeTabIcon;return u&&typeof u=="object"&&"value"in u?u.value:u},u=>{u!=null&&(f.icon=String(u),E())},{immediate:!0});h.push(y)}if(r.routeTabClosable!==void 0){const y=n.watch(()=>{const u=r.routeTabClosable;return u&&typeof u=="object"&&"value"in u?u.value:u},u=>{u!=null&&(f.closable=!!u,E())},{immediate:!0});h.push(y)}if(r.routeTabMeta!==void 0){const y=n.watch(()=>{const u=r.routeTabMeta;return u&&typeof u=="object"&&"value"in u?u.value:u},u=>{u&&typeof u=="object"&&(Object.assign(f,u),E())},{immediate:!0,deep:!0});h.push(y)}S.set(t,h)}function O(t){const r=S.get(t);r&&(r.forEach(f=>f()),S.delete(t)),K.delete(t)}function M(t,r){t?t.routeTabTitle!==void 0||t.routeTabIcon!==void 0||t.routeTabClosable!==void 0?j(r,t):t.$&&(t.$.routeTabTitle!==void 0||t.$.routeTabIcon!==void 0||t.$.routeTabClosable!==void 0)&&j(r,t.$):t===null&&O(r)}if(e.cookieKey!==null||e.persistence){const t={...e.persistence??{}};e.cookieKey!==null?t.cookieKey=e.cookieKey||Y:t.cookieKey||(t.cookieKey=Y),ee(t)}const oe=n.computed(()=>ue(e.tabTransition)),ae=n.computed(()=>ue(e.pageTransition)),T=n.reactive({visible:!1,target:null,position:{x:0,y:0}}),W=n.ref(null),x=n.ref([]),I=n.ref(-1),l=n.reactive({dragging:!1,dragIndex:-1,dropIndex:-1,dragTab:null}),b=["refresh","refreshAll","close","closeLefts","closeRights","closeOthers"];function m(t){return i.tabs.findIndex(r=>r.id===t)}function k(t){const r=m(t.id);return r>0?i.tabs.slice(0,r):[]}function C(t){const r=m(t.id);return r>-1?i.tabs.slice(r+1):[]}function P(t){return i.tabs.filter(r=>r.id!==t.id)}async function A(t,r){const f=t.filter(h=>h.closable!==!1);if(f.length){for(const h of f)i.activeId.value===h.id?await i.closeTab(h.id,{redirect:r.to,force:!0}):await i.removeTab(h.id,{force:!0});i.activeId.value!==r.id&&await i.openTab(r.to,!0,!1)}}const tt={refresh:{label:"Refresh",handler:async({target:t})=>{await i.refreshTab(t.id,!0)}},refreshAll:{label:"Refresh All",handler:async()=>{await i.refreshAll(!0)}},close:{label:"Close",handler:async({target:t})=>{await i.closeTab(t.id)},enable:({target:t})=>ie(t)},closeLefts:{label:"Close to the Left",handler:async({target:t})=>{await A(k(t),t)},enable:({target:t})=>k(t).some(r=>r.closable!==!1)},closeRights:{label:"Close to the Right",handler:async({target:t})=>{await A(C(t),t)},enable:({target:t})=>C(t).some(r=>r.closable!==!1)},closeOthers:{label:"Close Others",handler:async({target:t})=>{await A(P(t),t)},enable:({target:t})=>P(t).some(r=>r.closable!==!1)}};function B(){T.visible=!1,T.target=null,I.value=-1,x.value=[]}function nt(t,r){e.contextmenu&&(T.visible=!0,T.target=t,T.position.x=r.clientX,T.position.y=r.clientY,document.addEventListener("click",B,{once:!0}),n.nextTick(()=>{at()}))}function ot(t,r){const f=typeof t=="string"?{id:t}:t,h=tt[f.id],y=f.label??h?.label??String(f.id),u=f.visible??h?.visible??!0;if(!(typeof u=="function"?u(r):u!==!1))return null;const le=f.enable??h?.enable??!0,kt=typeof le=="function"?le(r):le!==!1,Ce=f.handler??h?.handler;if(!Ce)return null;const vt=async()=>{await Promise.resolve(Ce(r))};return{id:String(f.id),label:y,disabled:!kt,action:vt}}const U=n.computed(()=>{if(!T.visible||!T.target||e.contextmenu===!1)return[];const t=Array.isArray(e.contextmenu)?e.contextmenu:b,r={target:T.target,controller:i};return t.map(f=>ot(f,r)).filter(f=>!!f)});function at(){const t=W.value;if(!t)return;const r=8,{innerWidth:f,innerHeight:h}=window,y=t.getBoundingClientRect();let u=T.position.x,L=T.position.y;y.right>f-r&&(u=Math.max(r,f-y.width-r)),y.bottom>h-r&&(L=Math.max(r,h-y.height-r)),(u!==T.position.x||L!==T.position.y)&&(T.position.x=u,T.position.y=L)}function rt(t,r){x.value[r]=t??null}function it(t){if(t<0)return;x.value[t]?.focus({preventScroll:!0})}function q(t,r,f=U.value){if(!f.length)return-1;const h=f.length;let y=t;for(let u=0;u<h;u++)if(y=(y+r+h)%h,!f[y].disabled)return y;return-1}function z(t){I.value=t,!(t<0)&&n.nextTick(()=>it(t))}function Te(t){const r=q(I.value,t);r!==-1&&z(r)}function lt(t){if(!T.visible)return;const r=t.key,f=U.value;if(!f.length)return;if(r==="Tab"){B();return}if(["ArrowDown","ArrowUp","ArrowRight","ArrowLeft","Home","End","Enter"," ","Spacebar","Escape"].includes(r))switch(t.preventDefault(),r){case"ArrowDown":case"ArrowRight":Te(1);break;case"ArrowUp":case"ArrowLeft":Te(-1);break;case"Home":z(q(-1,1));break;case"End":z(q(f.length,-1));break;case"Enter":case" ":case"Spacebar":{const y=I.value;if(y>-1){const u=f[y];u.disabled||ke(u)}break}case"Escape":B();break}}async function ke(t){t.disabled||(B(),await t.action())}function re(t){return typeof t.title=="string"&&t.title.trim()?t.title:Array.isArray(t.title)&&t.title.length&&String(t.title[0]).trim()?String(t.title[0]):"Untitled"}function st(t){return g.value[t.id]||re(t)}function ve(t){const r=i.getRouteKey(t),h=i.tabs.find(y=>y.id===r)?.renderKey??0;return`${r}::${h}`}function ct(t){return`${ve(t)}::refresh`}function ie(t){return!(t.closable===!1||i.options.keepLastTab&&i.tabs.length<=1)}async function ut(t){await i.closeTab(t.id)}function dt(t){if(t.href&&typeof window<"u"){t.target&&t.target!=="_self"?window.open(t.href,t.target):window.location.assign(t.href);return}i.activeId.value!==t.id&&i.openTab(t.to,!1)}function ft(t){return["router-tab__item",{"is-active":i.activeId.value===t.id,"is-closable":ie(t),"is-dragging":l.dragging&&l.dragTab?.id===t.id,"is-drag-over":l.dropIndex===m(t.id)},t.tabClass]}function bt(t){return i.refreshingKey.value===i.getRouteKey(t)}function mt(t,r,f){e.sortable&&(l.dragging=!0,l.dragIndex=r,l.dragTab=t,f.dataTransfer&&(f.dataTransfer.effectAllowed="move",f.dataTransfer.setData("text/plain",t.id)),o("tab-sort",{tab:t,index:r}))}function pt(t,r){!e.sortable||!l.dragging||(r.preventDefault(),r.dataTransfer&&(r.dataTransfer.dropEffect="move"))}function gt(t){!e.sortable||!l.dragging||(l.dropIndex=t)}function ht(){!e.sortable||l.dragging}function yt(t,r){if(!(!e.sortable||!l.dragging)){if(r.preventDefault(),l.dragIndex!==-1&&l.dragIndex!==t){const f=i.tabs.splice(l.dragIndex,1)[0];i.tabs.splice(t,0,f),o("tab-sorted",{tab:f,fromIndex:l.dragIndex,toIndex:t})}we()}}function we(){l.dragging=!1,l.dragIndex=-1,l.dropIndex=-1,l.dragTab=null}n.onMounted(()=>{document.addEventListener("keydown",B)}),n.onBeforeUnmount(()=>{document.removeEventListener("keydown",B),a.appContext.config.globalProperties.$tabs=null,S.forEach(t=>{t.forEach(r=>r())}),S.clear(),K.clear()}),n.watch(()=>e.keepAlive,t=>{i.options.keepAlive=t}),n.watch(()=>i.activeId.value,()=>B()),n.watch(()=>e.contextmenu,t=>{t||B()}),n.watch(()=>U.value.length,t=>{T.visible&&t===0&&B()}),n.watch(U,t=>{if(x.value=new Array(t.length).fill(null),!T.visible)return;const r=q(-1,1,t);z(r)}),n.watch(()=>T.visible,t=>{t||(I.value=-1,x.value=[])});const Tt=i.includeKeys;return{controller:i,tabs:i.tabs,includeKeys:Tt,tabTransitionProps:oe,pageTransitionProps:ae,buildTabClass:ft,activate:dt,close:ut,context:T,menuItems:U,handleMenuAction:ke,showContextMenu:nt,hideContextMenu:B,getTabTitle:re,isClosable:ie,isRefreshing:bt,hasCustomSlot:p,hasStartSlot:v,hasEndSlot:c,onDragStart:mt,onDragOver:pt,onDragEnter:gt,onDragLeave:ht,onDrop:yt,onDragEnd:we,setupComponentWatching:j,cleanupComponentWatching:O,handleComponentRef:M,getReactiveTabTitle:st,getComponentCacheKey:ve,getRefreshComponentKey:ct,triggerTabUpdate:E,menuRef:W,highlightedIndex:I,setMenuItemRef:rt,onMenuKeydown:lt,highlightMenuIndex:z}}}),Me=(e,o)=>{const a=e.__vccOpts||e;for(const[s,i]of o)a[s]=i;return a},Le={class:"router-tab"},Ve={class:"router-tab__header"},Ne={class:"router-tab__scroll"},je=["data-title","draggable","onClick","onAuxclick","onContextmenu","onDragstart","onDragover","onDragenter","onDrop"],Oe=["title"],Ue=["onClick"],ze={class:"router-tab__container"},_e=["aria-disabled","disabled","tabindex","onMouseenter","onClick"];function Ye(e,o,a,s,i,p){const v=n.resolveComponent("RouterView");return n.openBlock(),n.createElementBlock("div",Le,[n.createElementVNode("header",Ve,[n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-start",{"has-content":e.hasStartSlot}])},[n.renderSlot(e.$slots,"start")],2),n.createElementVNode("div",Ne,[n.createVNode(n.TransitionGroup,n.mergeProps({tag:"ul",class:"router-tab__nav"},e.tabTransitionProps),{default:n.withCtx(()=>[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(e.tabs,(c,d)=>(n.openBlock(),n.createElementBlock("li",{key:c.id,class:n.normalizeClass(e.buildTabClass(c)),"data-title":e.getTabTitle(c),draggable:e.sortable,onClick:g=>e.activate(c),onAuxclick:n.withModifiers(g=>e.close(c),["middle","prevent"]),onContextmenu:n.withModifiers(g=>e.showContextMenu(c,g),["prevent"]),onDragstart:g=>e.onDragStart(c,d,g),onDragover:g=>e.onDragOver(d,g),onDragenter:g=>e.onDragEnter(d),onDragleave:o[0]||(o[0]=(...g)=>e.onDragLeave&&e.onDragLeave(...g)),onDrop:g=>e.onDrop(d,g),onDragend:o[1]||(o[1]=(...g)=>e.onDragEnd&&e.onDragEnd(...g))},[c.icon?(n.openBlock(),n.createElementBlock("i",{key:0,class:n.normalizeClass(["router-tab__item-icon",c.icon])},null,2)):n.createCommentVNode("",!0),n.createElementVNode("span",{class:"router-tab__item-title",title:e.getReactiveTabTitle(c)},n.toDisplayString(e.getReactiveTabTitle(c)),9,Oe),e.isClosable(c)?(n.openBlock(),n.createElementBlock("a",{key:1,class:"router-tab__item-close",onClick:n.withModifiers(g=>e.close(c),["stop"])},null,8,Ue)):n.createCommentVNode("",!0)],42,je))),128))]),_:1},16)]),n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-end",{"has-content":e.hasEndSlot}])},[n.renderSlot(e.$slots,"end")],2)]),n.createElementVNode("div",ze,[n.createVNode(v,null,{default:n.withCtx(c=>[e.hasCustomSlot?n.renderSlot(e.$slots,"default",n.normalizeProps(n.mergeProps({key:0},{...c,controller:e.controller,pageRef:d=>e.handleComponentRef(d,e.controller.getRouteKey(c.route))}))):(n.openBlock(),n.createElementBlock(n.Fragment,{key:1},[n.createVNode(n.Transition,n.mergeProps(e.pageTransitionProps,{appear:""}),{default:n.withCtx(()=>[e.controller.options.keepAlive?(n.openBlock(),n.createBlock(n.KeepAlive,{key:0,include:e.includeKeys,max:e.controller.options.maxAlive||void 0},[e.isRefreshing(c.route)?n.createCommentVNode("",!0):(n.openBlock(),n.createBlock(n.resolveDynamicComponent(c.Component),{key:e.getComponentCacheKey(c.route),ref:d=>e.handleComponentRef(d,e.controller.getRouteKey(c.route)),class:"router-tab-page"}))],1032,["include","max"])):n.createCommentVNode("",!0)]),_:2},1040),n.createVNode(n.Transition,n.mergeProps(e.pageTransitionProps,{appear:""}),{default:n.withCtx(()=>[!e.controller.options.keepAlive||e.isRefreshing(c.route)?(n.openBlock(),n.createBlock(n.resolveDynamicComponent(c.Component),{key:e.getRefreshComponentKey(c.route),ref:d=>e.handleComponentRef(d,e.controller.getRouteKey(c.route)),class:"router-tab-page"})):n.createCommentVNode("",!0)]),_:2},1040)],64))]),_:3})]),e.context.visible&&e.context.target?(n.openBlock(),n.createElementBlock("div",{key:0,ref:"menuRef",class:"router-tab__contextmenu",role:"menu",onKeydown:o[2]||(o[2]=(...c)=>e.onMenuKeydown&&e.onMenuKeydown(...c)),style:n.normalizeStyle({left:e.context.position.x+"px",top:e.context.position.y+"px"})},[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(e.menuItems,(c,d)=>(n.openBlock(),n.createElementBlock("a",{key:c.id,role:"menuitem",class:n.normalizeClass(["router-tab__contextmenu-item",{"is-focused":d===e.highlightedIndex}]),"aria-disabled":c.disabled,disabled:c.disabled,tabindex:c.disabled?-1:d===e.highlightedIndex?0:-1,ref_for:!0,ref:g=>e.setMenuItemRef(g,d),onMouseenter:g=>!c.disabled&&e.highlightMenuIndex(d),onClick:g=>e.handleMenuAction(c)},n.toDisplayString(c.label),43,_e))),128))],36)):n.createCommentVNode("",!0)])}const te=Me($e,[["render",Ye]]),Fe={class:"router-tabs","aria-hidden":"true"},F=n.defineComponent({name:"RouterTabs",__name:"RouterTabs",props:{cookieKey:{},expiresInDays:{},path:{},domain:{},secure:{type:Boolean},sameSite:{},serialize:{type:Function},deserialize:{type:Function},fallbackRoute:{}},setup(e){return ee(e),(a,s)=>(n.openBlock(),n.createElementBlock("span",Fe))}}),be="tab-theme-style",me="tab-theme-primary-color",He="system",pe="(prefers-color-scheme: dark)";let N=null;const R={primary:"#034960",background:"#ffffff",text:"#1e293b",border:"#e2e8f0",activeBackground:"#034960",activeText:"#ffffff",activeBorder:"#034960",headerBackground:"#ffffff",buttonBackground:"#f8fafc",buttonColor:"#034960",activeButtonBackground:"#034960",activeButtonColor:"#ffffff",iconColor:"#475569"},Je={primary:"#38bdf8",background:"#0f172a",text:"#f1f5f9",border:"#334155",activeBackground:"#1e293b",activeText:"#38bdf8",activeBorder:"#38bdf8",headerBackground:"#0c4a6e",buttonBackground:"#1e293b",buttonColor:"#f1f5f9",activeButtonBackground:"#38bdf8",activeButtonColor:"#0f172a",iconColor:"#cbd5e1"};function We(e){if(typeof window>"u")return null;const o=window.localStorage.getItem(e);if(!o)return null;try{const a=JSON.parse(o);return a&&typeof a=="object"?a:null}catch{return null}}function ne(e){typeof document>"u"||(document.documentElement.style.setProperty("--router-tab-primary",e.primary??R.primary),document.documentElement.style.setProperty("--router-tab-header-bg",e.headerBackground??R.headerBackground),document.documentElement.style.setProperty("--router-tab-background",e.background??R.background),document.documentElement.style.setProperty("--router-tab-active-background",e.activeBackground??R.activeBackground),document.documentElement.style.setProperty("--router-tab-text",e.text??R.text),document.documentElement.style.setProperty("--router-tab-active-text",e.activeText??R.activeText),document.documentElement.style.setProperty("--router-tab-border",e.border??R.border),document.documentElement.style.setProperty("--router-tab-active-border",e.activeBorder??R.activeBorder),document.documentElement.style.setProperty("--router-tab-button-color",e.buttonColor??R.buttonColor),document.documentElement.style.setProperty("--router-tab-active-button-color",e.activeButtonColor??R.activeButtonColor),document.documentElement.style.setProperty("--router-tab-button-background",e.buttonBackground??R.buttonBackground),document.documentElement.style.setProperty("--router-tab-active-button-background",e.activeButtonBackground??R.activeButtonBackground),document.documentElement.style.setProperty("--router-tab-icon-color",e.iconColor??R.iconColor))}function ge(e){if(typeof document>"u")return;const o=document.documentElement,a=window.matchMedia(pe),s=()=>{o.dataset.theme=a.matches?"dark":"light"};N&&(a.removeEventListener("change",N),N=null),e==="system"?(s(),N=()=>s(),a.addEventListener("change",N)):o.dataset.theme=e}function he(e={}){if(typeof window>"u")return;const{styleKey:o=be,primaryKey:a=me,defaultStyle:s=He,defaultPrimary:i}=e,p=window.localStorage.getItem(o)??s;ge(p);const c=p==="dark"||p==="system"&&window.matchMedia(pe).matches?{...Je}:{...R};i&&(c.primary=i);const d=We(a);ne(d?{...c,...d}:c)}function qe(e,o){if(typeof window>"u")return;const a=o?.styleKey??be;window.localStorage.setItem(a,e),ge(e)}function Ge(e,o){if(typeof window>"u")return;const a=o?.primaryKey??me;window.localStorage.setItem(a,JSON.stringify(e)),ne(e)}function H(e,o){if(n.isRef(e)){const s=!n.isReadonly(e);return{value:e,update:s?i=>{e.value=i}:()=>{}}}if(typeof e=="function"){const s=e;return{value:n.computed(s),update:()=>{}}}const a=n.ref(e===void 0?o:e);return{value:a,update:s=>{a.value=s}}}function J(e={}){const o=H(e.title,"Untitled"),a=H(e.icon,""),s=H(e.closable,!0),i=H(e.meta,{});return{routeTabTitle:o.value,routeTabIcon:a.value,routeTabClosable:s.value,routeTabMeta:i.value,updateTitle:o.update,updateIcon:a.update,updateClosable:s.update,updateMeta:i.update}}function Xe(e,o="Page"){return J({title:n.computed(()=>e.value?"Loading...":o),icon:n.computed(()=>e.value?"mdi-loading mdi-spin":"mdi-page"),closable:n.computed(()=>!e.value)})}function Qe(e,o="Page",a="mdi-page"){return J({title:n.computed(()=>e.value>0?`${o} (${e.value})`:o),icon:n.computed(()=>e.value>0?"mdi-bell-badge":a)})}function Ze(e,o="Page"){const a={normal:{suffix:"",icon:"mdi-page"},loading:{suffix:" - Loading",icon:"mdi-loading mdi-spin"},error:{suffix:" - Error",icon:"mdi-alert"},success:{suffix:" - Success",icon:"mdi-check-circle"}};return J({title:n.computed(()=>o+a[e.value].suffix),icon:n.computed(()=>a[e.value].icon),closable:n.computed(()=>e.value!=="loading")})}let ye=!1;const et={install(e,o){if(ye)return;ye=!0;const{initTheme:a=!0,themeOptions:s,componentName:i=te.name||"RouterTab",tabsComponentName:p=F.name||"RouterTabs"}=o??{};a&&he(s??{}),e.component(i,te),e.component(p,F),p.toLowerCase()!=="router-tabs"&&e.component("router-tabs",F),Object.defineProperty(e.config.globalProperties,"$tabs",{configurable:!0,enumerable:!1,get(){return e._context.provides[V]},set(v){v&&e.provide(V,v)}})}};w.RouterTab=te,w.RouterTabs=F,w.default=et,w.initRouterTabsTheme=he,w.routerTabsKey=V,w.setRouterTabsPrimary=Ge,w.setRouterTabsTheme=qe,w.useLoadingTab=Xe,w.useNotificationTab=Qe,w.useReactiveTab=J,w.useRouterTabs=Z,w.useRouterTabsPersistence=ee,w.useStatusTab=Ze,Object.defineProperties(w,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
@@ -76,7 +76,7 @@
76
76
  <component
77
77
  v-if="!isRefreshing(routerSlot.route)"
78
78
  :is="routerSlot.Component"
79
- :key="controller.getRouteKey(routerSlot.route)"
79
+ :key="getComponentCacheKey(routerSlot.route)"
80
80
  :ref="(el: any) => handleComponentRef(el, controller.getRouteKey(routerSlot.route))"
81
81
  class="router-tab-page"
82
82
  />
@@ -90,7 +90,7 @@
90
90
  <component
91
91
  v-if="!controller.options.keepAlive || isRefreshing(routerSlot.route)"
92
92
  :is="routerSlot.Component"
93
- :key="controller.getRouteKey(routerSlot.route) + (isRefreshing(routerSlot.route) ? '-refresh' : '')"
93
+ :key="getRefreshComponentKey(routerSlot.route)"
94
94
  :ref="(el: any) => handleComponentRef(el, controller.getRouteKey(routerSlot.route))"
95
95
  class="router-tab-page"
96
96
  />
@@ -716,6 +716,17 @@ export default defineComponent({
716
716
  return reactiveTitles[tab.id] || getTabTitle(tab)
717
717
  }
718
718
 
719
+ function getComponentCacheKey(route: RouteLocationNormalizedLoaded): string {
720
+ const routeKey = controller.getRouteKey(route)
721
+ const tab = controller.tabs.find(item => item.id === routeKey)
722
+ const renderKey = tab?.renderKey ?? 0
723
+ return `${routeKey}::${renderKey}`
724
+ }
725
+
726
+ function getRefreshComponentKey(route: RouteLocationNormalizedLoaded): string {
727
+ return `${getComponentCacheKey(route)}::refresh`
728
+ }
729
+
719
730
  function isClosable(tab: TabRecord) {
720
731
  if (tab.closable === false) return false
721
732
  if (controller.options.keepLastTab && controller.tabs.length <= 1) return false
@@ -910,6 +921,8 @@ export default defineComponent({
910
921
  cleanupComponentWatching,
911
922
  handleComponentRef,
912
923
  getReactiveTabTitle,
924
+ getComponentCacheKey,
925
+ getRefreshComponentKey,
913
926
  triggerTabUpdate,
914
927
  menuRef,
915
928
  highlightedIndex,
@@ -96,6 +96,7 @@ function createTabFromRoute(
96
96
  alive: resolveAlive(route, keepAliveDefault),
97
97
  reusable: resolveReusable(route, false),
98
98
  closable: meta.closable ?? true,
99
+ renderKey: typeof base.renderKey === 'number' ? base.renderKey : 0,
99
100
  ...meta,
100
101
  ...base
101
102
  }
@@ -134,7 +135,8 @@ function toSnapshotTab(tab: TabRecord): RouterTabsSnapshotTab {
134
135
  tips: tab.tips,
135
136
  icon: tab.icon,
136
137
  tabClass: tab.tabClass,
137
- closable: tab.closable
138
+ closable: tab.closable,
139
+ renderKey: tab.renderKey
138
140
  }
139
141
  }
140
142
 
@@ -145,6 +147,9 @@ function fromSnapshotTab(snapshot: RouterTabsSnapshotTab): Partial<TabRecord> {
145
147
  if ('icon' in snapshot) base.icon = snapshot.icon
146
148
  if ('tabClass' in snapshot) base.tabClass = snapshot.tabClass
147
149
  if ('closable' in snapshot) base.closable = snapshot.closable
150
+ if ('renderKey' in snapshot && typeof snapshot.renderKey === 'number') {
151
+ base.renderKey = snapshot.renderKey
152
+ }
148
153
  return base
149
154
  }
150
155
 
@@ -158,7 +163,11 @@ export function createRouterTabs(
158
163
  const activeId = ref<string | null>(null)
159
164
  const current = shallowRef<TabRecord>()
160
165
  const refreshingKey = ref<string | null>(null)
161
- const includeKeys = computed(() => tabs.filter(tab => tab.alive).map(tab => tab.id))
166
+ const includeKeys = computed(() =>
167
+ tabs
168
+ .filter(tab => tab.alive)
169
+ .map(tab => `${tab.id}::${tab.renderKey}`)
170
+ )
162
171
 
163
172
  let isHydrating = false
164
173
 
@@ -270,9 +279,16 @@ export function createRouterTabs(
270
279
  const tab = tabs.find(item => item.id === id)
271
280
  if (!tab) return
272
281
 
273
- if (options.keepAlive && tab.alive) {
282
+ const wasAlive = options.keepAlive && tab.alive
283
+
284
+ if (wasAlive) {
274
285
  tab.alive = false
275
286
  await nextTick()
287
+ }
288
+
289
+ tab.renderKey = (tab.renderKey ?? 0) + 1
290
+
291
+ if (wasAlive) {
276
292
  tab.alive = true
277
293
  await nextTick()
278
294
  }
package/lib/core/types.ts CHANGED
@@ -23,6 +23,7 @@ export interface TabMeta {
23
23
  export interface TabInput extends Partial<TabMeta> {
24
24
  to: RouteLocationRaw
25
25
  id?: string
26
+ renderKey?: number
26
27
  }
27
28
 
28
29
  export interface TabRecord extends TabMeta {
@@ -32,6 +33,7 @@ export interface TabRecord extends TabMeta {
32
33
  matched: RouteLocationNormalizedLoaded
33
34
  alive: boolean
34
35
  reusable: boolean
36
+ renderKey: number
35
37
  }
36
38
 
37
39
  export type RouterTabsMenuPreset =
@@ -64,6 +66,7 @@ export interface RouterTabsSnapshotTab {
64
66
  icon?: TabRecord['icon']
65
67
  tabClass?: TabRecord['tabClass']
66
68
  closable?: boolean
69
+ renderKey?: number
67
70
  }
68
71
 
69
72
  export interface RouterTabsSnapshot {
@@ -149,7 +149,8 @@ export function useRouterTabsPersistence(options: RouterTabsPersistenceOptions =
149
149
  tips: tab.tips,
150
150
  icon: tab.icon,
151
151
  tabClass: tab.tabClass,
152
- closable: tab.closable
152
+ closable: tab.closable,
153
+ renderKey: tab.renderKey
153
154
  })),
154
155
  active: ctrl.activeId.value
155
156
  }),
@@ -310,3 +310,251 @@
310
310
  }
311
311
  }
312
312
  }
313
+
314
+ // =============================================================================
315
+ // Transition Presets
316
+ // =============================================================================
317
+
318
+ // Tab list animation (used for transition-group on nav items)
319
+ .router-tab-zoom-enter-active,
320
+ .router-tab-zoom-leave-active {
321
+ transition:
322
+ transform $router-tab-transition-fast,
323
+ opacity $router-tab-transition-fast;
324
+ }
325
+
326
+ .router-tab-zoom-enter-from,
327
+ .router-tab-zoom-leave-to {
328
+ transform: scale(0.9);
329
+ opacity: 0;
330
+ }
331
+
332
+ .router-tab-zoom-enter-to,
333
+ .router-tab-zoom-leave-from {
334
+ transform: scale(1);
335
+ opacity: 1;
336
+ }
337
+
338
+ .router-tab-zoom-move {
339
+ transition: transform $router-tab-transition-fast;
340
+ }
341
+
342
+ // Page swap animation (default for routed content)
343
+ .router-tab-swap-enter-active,
344
+ .router-tab-swap-leave-active {
345
+ position: relative;
346
+ transition:
347
+ opacity 0.5s ease,
348
+ transform 0.5s ease;
349
+ will-change: opacity, transform;
350
+ }
351
+
352
+ .router-tab-swap-enter-from {
353
+ opacity: 0;
354
+ transform: translateY(20px);
355
+ }
356
+
357
+ .router-tab-swap-enter-to {
358
+ opacity: 1;
359
+ transform: translateY(0);
360
+ }
361
+
362
+ .router-tab-swap-leave-from {
363
+ opacity: 1;
364
+ transform: translateY(0);
365
+ }
366
+
367
+ .router-tab-swap-leave-to {
368
+ opacity: 0;
369
+ transform: translateY(-15px);
370
+ }
371
+
372
+ // =============================================================================
373
+ // Additional Page Transition Effects
374
+ // =============================================================================
375
+
376
+ // Slide transition - slides horizontally
377
+ .router-tab-slide-enter-active,
378
+ .router-tab-slide-leave-active {
379
+ position: relative;
380
+ transition:
381
+ opacity 0.5s ease,
382
+ transform 0.5s ease;
383
+ will-change: opacity, transform;
384
+ }
385
+
386
+ .router-tab-slide-enter-from {
387
+ opacity: 0;
388
+ transform: translateX(80px);
389
+ }
390
+
391
+ .router-tab-slide-enter-to {
392
+ opacity: 1;
393
+ transform: translateX(0);
394
+ }
395
+
396
+ .router-tab-slide-leave-from {
397
+ opacity: 1;
398
+ transform: translateX(0);
399
+ }
400
+
401
+ .router-tab-slide-leave-to {
402
+ opacity: 0;
403
+ transform: translateX(-80px);
404
+ }
405
+
406
+ // Fade transition - smooth fade in and out
407
+ .router-tab-fade-enter-active {
408
+ transition: opacity 0.8s ease-in;
409
+ will-change: opacity;
410
+ }
411
+
412
+ .router-tab-fade-leave-active {
413
+ transition: opacity 0.6s ease-out;
414
+ will-change: opacity;
415
+ }
416
+
417
+ .router-tab-fade-enter-from {
418
+ opacity: 0;
419
+ }
420
+
421
+ .router-tab-fade-enter-to {
422
+ opacity: 1;
423
+ }
424
+
425
+ .router-tab-fade-leave-from {
426
+ opacity: 1;
427
+ }
428
+
429
+ .router-tab-fade-leave-to {
430
+ opacity: 0;
431
+ }
432
+
433
+ // Scale transition - zoom in/out effect
434
+ .router-tab-scale-enter-active,
435
+ .router-tab-scale-leave-active {
436
+ position: relative;
437
+ transition:
438
+ opacity 0.6s ease,
439
+ transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
440
+ will-change: opacity, transform;
441
+ }
442
+
443
+ .router-tab-scale-enter-from {
444
+ opacity: 0;
445
+ transform: scale(0.7);
446
+ }
447
+
448
+ .router-tab-scale-enter-to {
449
+ opacity: 1;
450
+ transform: scale(1);
451
+ }
452
+
453
+ .router-tab-scale-leave-from {
454
+ opacity: 1;
455
+ transform: scale(1);
456
+ }
457
+
458
+ .router-tab-scale-leave-to {
459
+ opacity: 0;
460
+ transform: scale(1.2);
461
+ }
462
+
463
+ // Flip transition - 3D flip effect
464
+ .router-tab-flip-enter-active,
465
+ .router-tab-flip-leave-active {
466
+ position: relative;
467
+ transition:
468
+ opacity 0.6s ease,
469
+ transform 0.6s ease;
470
+ transform-style: preserve-3d;
471
+ will-change: opacity, transform;
472
+ }
473
+
474
+ .router-tab-flip-enter-from {
475
+ opacity: 0;
476
+ transform: rotateX(-90deg);
477
+ }
478
+
479
+ .router-tab-flip-enter-to {
480
+ opacity: 1;
481
+ transform: rotateX(0);
482
+ }
483
+
484
+ .router-tab-flip-leave-from {
485
+ opacity: 1;
486
+ transform: rotateX(0);
487
+ }
488
+
489
+ .router-tab-flip-leave-to {
490
+ opacity: 0;
491
+ transform: rotateX(90deg);
492
+ }
493
+
494
+ // Rotate transition - rotate and fade
495
+ .router-tab-rotate-enter-active,
496
+ .router-tab-rotate-leave-active {
497
+ position: relative;
498
+ transition:
499
+ opacity 0.6s ease,
500
+ transform 0.6s ease;
501
+ will-change: opacity, transform;
502
+ }
503
+
504
+ .router-tab-rotate-enter-from {
505
+ opacity: 0;
506
+ transform: rotate(-8deg) scale(0.85);
507
+ }
508
+
509
+ .router-tab-rotate-enter-to {
510
+ opacity: 1;
511
+ transform: rotate(0) scale(1);
512
+ }
513
+
514
+ .router-tab-rotate-leave-from {
515
+ opacity: 1;
516
+ transform: rotate(0) scale(1);
517
+ }
518
+
519
+ .router-tab-rotate-leave-to {
520
+ opacity: 0;
521
+ transform: rotate(8deg) scale(0.85);
522
+ }
523
+
524
+ // Bounce transition - elastic bounce effect
525
+ .router-tab-bounce-enter-active {
526
+ animation: router-tab-bounce-in 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
527
+ }
528
+
529
+ .router-tab-bounce-leave-active {
530
+ animation: router-tab-bounce-out 0.5s ease-in;
531
+ }
532
+
533
+ @keyframes router-tab-bounce-in {
534
+ 0% {
535
+ opacity: 0;
536
+ transform: scale(0.3) translateY(-80px);
537
+ }
538
+ 50% {
539
+ opacity: 1;
540
+ transform: scale(1.1);
541
+ }
542
+ 70% {
543
+ transform: scale(0.9);
544
+ }
545
+ 100% {
546
+ opacity: 1;
547
+ transform: scale(1) translateY(0);
548
+ }
549
+ }
550
+
551
+ @keyframes router-tab-bounce-out {
552
+ 0% {
553
+ opacity: 1;
554
+ transform: scale(1);
555
+ }
556
+ 100% {
557
+ opacity: 0;
558
+ transform: scale(0.7) translateY(30px);
559
+ }
560
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue3-router-tab",
3
- "version": "1.2.7",
3
+ "version": "1.2.8",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",