vue3-router-tab 1.4.0 → 1.4.2

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(E,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):(E=typeof globalThis<"u"?globalThis:E||self,n(E["vue3-router-tab"]={},E.Vue,E.VueRouter))})(this,(function(E,n,De){"use strict";function xe(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 x(e,a){const r=e.resolve(a);if(!r||!r.matched.length)throw new Error(`[RouterTabs] Unable to resolve route: ${String(a)}`);return r}const Me={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 M(e){const a=e.meta?.key;if(typeof a=="function"){const r=a(e);if(typeof r=="string"&&r.length)return r}else if(typeof a=="string"&&a.length){const r=Me[a.toLowerCase()];return r?r(e):a}return e.fullPath}function te(e,a){const r=e.meta?.keepAlive;return typeof r=="boolean"?r:a}function ne(e,a){const r=e.meta?.reuse;return typeof r=="boolean"?r:a}function he(e){const a=e.meta??{},r={};return"title"in a&&(r.title=a.title),"tips"in a&&(r.tips=a.tips),"icon"in a&&(r.icon=a.icon),"closable"in a&&(r.closable=a.closable),"tabClass"in a&&(r.tabClass=a.tabClass),"target"in a&&(r.target=a.target),"href"in a&&(r.href=a.href),r}function W(e,a,r){const c=he(e);return{id:M(e),to:e.fullPath,fullPath:e.fullPath,matched:e,alive:te(e,r),reusable:ne(e,!1),closable:c.closable??!0,renderKey:typeof a.renderKey=="number"?a.renderKey:0,...c,...a}}function oe(e,a,r,c){if(!e.find(h=>h.id===a.id)){if(r==="next"&&c){const h=e.findIndex(k=>k.id===c);if(h!==-1){e.splice(h+1,0,a);return}}e.push(a)}}function re(e,a,r,c){if(!a||a<=0)return;const i=e.filter(h=>h.alive);for(;i.length>a;){const h=i.shift();if(!h||h.id===r)continue;const k=e.findIndex(s=>s.id===h.id);if(k>-1){const s=e[k],b=`${s.id}::${s.renderKey??0}`;c.delete(b),s.alive=!1}}}function Le(e){return{to:e.to,title:e.title,tips:e.tips,icon:e.icon,tabClass:e.tabClass,closable:e.closable,renderKey:e.renderKey}}function Ve(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),"renderKey"in e&&typeof e.renderKey=="number"&&(a.renderKey=e.renderKey),a}function Ne(e,a={}){const r=xe(a),c=n.reactive([]),i=n.ref(null),h=n.shallowRef(),k=n.ref(null),s=n.reactive(new Set),b=n.computed(()=>Array.from(s));let y=!1;function $(u){const m=typeof u.matched=="object"?u:x(e,u);return{key:M(m),fullPath:m.fullPath,alive:te(m,r.keepAlive),reusable:ne(m,!1),matched:m}}function B(u){const m=M(u);let f=c.find(v=>v.id===m);const T=te(u,r.keepAlive);if(f){f.fullPath=u.fullPath,f.to=u.fullPath,f.matched=u,f.reusable=ne(u,f.reusable),typeof f.renderKey!="number"&&(f.renderKey=0);const v=`${m}::${f.renderKey}`;return T?s.has(v)?f.alive||(f.alive=!0):(s.add(v),f.alive=!0):f.alive&&(s.delete(v),f.alive=!1),Object.assign(f,he(u)),f}if(f=W(u,{},r.keepAlive),f.alive){const v=`${m}::${f.renderKey??0}`;s.add(v)}return oe(c,f,r.appendPosition,i.value),re(c,r.maxAlive,i.value,s),f}async function A(u,m=!1,f="sameTab"){const T=x(e,u),v=M(T),R=i.value===v;f==="sameTab"&&(f=R),f&&await L(v,!0),await e[m?"replace":"push"](T),R&&await V()}function U(u){const m=c.findIndex(P=>P.id===u);if(m===-1)return r.defaultRoute;const f=c[m+1],T=c[m-1],v=c.find(P=>P.id!==u),R=f||T||v;return R?R.to:r.defaultRoute}async function I(u=i.value,m={}){if(!u)return;if(!m.force&&r.keepLastTab&&c.length===1)throw new Error("[RouterTabs] Unable to close the final tab when keepLastTab is true.");const T=i.value===u&&m.redirect!==null,v=T?m.redirect??U(u):null;await z(u,{force:m.force}),m.redirect!==null&&T&&v&&await e.replace(v)}async function z(u,m={}){const f=c.findIndex(R=>R.id===u);if(f===-1)return;const T=c[f],v=`${u}::${T.renderKey??0}`;s.delete(v),T.alive=!1,c.splice(f,1),k.value===u&&(k.value=null),i.value===u&&(i.value=null,h.value=void 0)}async function L(u=i.value??void 0,m=!1){if(!u)return;const f=c.find(P=>P.id===u);if(!f)return;const T=r.keepAlive&&f.alive,v=`${u}::${f.renderKey??0}`;T&&(s.delete(v),f.alive=!1,await n.nextTick()),f.renderKey=(f.renderKey??0)+1;const R=`${u}::${f.renderKey}`;T&&(s.add(R),f.alive=!0),k.value=u,await n.nextTick(),m||await n.nextTick(),k.value=null}async function ce(u=!1){for(const m of c)await L(m.id,u)}function w(u,m){const f=c.find(v=>v.id===u);if(!f)return;const T=`${u}::${f.renderKey??0}`;m?(s.add(T),f.alive=!0,re(c,r.maxAlive,i.value,s)):(s.delete(T),f.alive=!1)}function Q(u){const m=c.find(T=>T.id===u);if(!m)return;const f=`${u}::${m.renderKey??0}`;s.delete(f),m.alive=!1,m.renderKey=(m.renderKey??0)+1}function D(){s.clear(),c.forEach(u=>{u.alive=!1})}function S(){return b.value.slice()}async function Z(u=r.defaultRoute){c.splice(0,c.length),i.value=null,h.value=void 0;for(const m of r.initialTabs){const f=x(e,m.to),T=W(f,m,r.keepAlive);c.push(T)}await e.replace(u)}async function V(){const u=i.value;u&&await L(u,!0)}function C(u){return typeof u.matched=="object"?M(u):M(x(e,u))}function ue(){const u=c.find(m=>m.id===i.value);return{tabs:c.map(Le),active:u?u.to:null}}async function _(u){y=!0,c.splice(0,c.length),i.value=null,h.value=void 0;const m=u?.tabs??[];for(const T of m)try{const v=x(e,T.to),R=Ve(T),P=W(v,R,r.keepAlive);oe(c,P,"last",null)}catch(v){console.warn("[RouterTabs] Failed to restore tab",T,v)}y=!1;const f=u?.active??m[m.length-1]?.to??r.defaultRoute;if(f)try{await e.replace(f)}catch(T){console.warn("[RouterTabs] Failed to navigate to restored route",f,T)}}return n.watch(()=>e.currentRoute.value,u=>{if(y)return;const m=B(u);i.value=m.id,h.value=m,re(c,r.maxAlive,i.value,s)},{immediate:!0}),r.initialTabs.length&&r.initialTabs.forEach(u=>{const m=x(e,u.to),f=W(m,u,r.keepAlive);oe(c,f,"last",null)}),{options:r,tabs:c,activeId:i,current:h,includeKeys:b,refreshingKey:k,openTab:A,closeTab:I,removeTab:z,refreshTab:L,refreshAll:ce,setTabAlive:w,evictCache:Q,clearCache:D,getCacheKeys:S,reset:Z,reload:V,getRouteKey:C,matchRoute:$,snapshot:ue,hydrate:_,ensureTab:B}}function ye(e){return e?typeof e=="string"?{name:e}:e:{}}const O=Symbol("RouterTabsContext"),J="router-tabs:snapshot";function ae(e={}){const{optional:a=!1}=e,r=n.inject(O,null);if(r)return r;const c=n.inject("$tabs",null);if(c)return c;const h=n.getCurrentInstance()?.appContext.config.globalProperties.$tabs;if(h)return h;if(!a)throw new Error("[RouterTabs] useRouterTabs must be used within <router-tab>.");return null}const Oe=864e5;function je(e){if(typeof document>"u")return null;const a=`${encodeURIComponent(e)}=`,r=document.cookie?document.cookie.split("; "):[];for(const c of r)if(c.startsWith(a))return decodeURIComponent(c.slice(a.length));return null}function Te(e,a,r){if(typeof document>"u")return;const{expiresInDays:c=7,path:i="/",domain:h,secure:k,sameSite:s="lax"}=r,b=[`${encodeURIComponent(e)}=${encodeURIComponent(a)}`];if(c!==1/0){const y=new Date(Date.now()+c*Oe).toUTCString();b.push(`Expires=${y}`)}i&&b.push(`Path=${i}`),h&&b.push(`Domain=${h}`),k&&b.push("Secure"),s&&b.push(`SameSite=${s.charAt(0).toUpperCase()}${s.slice(1)}`),document.cookie=b.join("; ")}function ve(e,a){if(typeof document>"u")return;const{path:r="/",domain:c}=a,i=[`${encodeURIComponent(e)}=`];i.push("Expires=Thu, 01 Jan 1970 00:00:01 GMT"),r&&i.push(`Path=${r}`),c&&i.push(`Domain=${c}`),document.cookie=i.join("; ")}const Ue=e=>JSON.stringify(e??null),ze=e=>{if(!e)return null;try{return JSON.parse(e)}catch{return null}};function ie(e={}){const{cookieKey:a=J,serialize:r=Ue,deserialize:c=ze}=e,i=ae({optional:!0}),h=n.ref(!0),k=s=>{n.onMounted(async()=>{const b=c(je(a));if(b&&b.tabs?.length)try{if(h.value=!0,await s.hydrate(b),b.active){await n.nextTick();const $=s.tabs.find(B=>B.to===b.active);$&&(s.activeId.value=$.id,s.current.value=$)}}finally{h.value=!1}else if(Object.prototype.hasOwnProperty.call(e,"fallbackRoute"))try{h.value=!0;const $=e.fallbackRoute??s.options.defaultRoute;await s.reset($)}finally{h.value=!1}else h.value=!1;const y=s.snapshot();y.tabs.length?Te(a,r(y),e):ve(a,e),h.value=!1}),n.watch(()=>({tabs:s.tabs.map(b=>({to:b.to,title:b.title,tips:b.tips,icon:b.icon,tabClass:b.tabClass,closable:b.closable,renderKey:b.renderKey})),active:s.activeId.value}),()=>{if(h.value)return;const b=s.snapshot();b.tabs.length?Te(a,r(b),e):ve(a,e)},{deep:!0})};i?k(i):n.onMounted(()=>{const s=ae({optional:!0});s&&k(s)})}const _e=n.defineComponent({name:"RouterTab",components:{RouterView:De.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:J},persistence:{type:Object,default:null},sortable:{type:Boolean,default:!0}},emits:["tab-sort","tab-sorted"],setup(e,{emit:a}){const r=n.getCurrentInstance();if(!r)throw new Error("[RouterTab] component must be used within a Vue application context.");const c=r.appContext.app.config.globalProperties.$router;if(!c)throw new Error("[RouterTab] Vue Router is required. Make sure to call app.use(router) before RouterTab.");const i=Ne(c,{initialTabs:e.tabs,keepAlive:e.keepAlive,maxAlive:e.maxAlive,keepLastTab:e.keepLastTab,appendPosition:e.append,defaultRoute:e.defaultPage});n.provide(O,i),r.appContext.config.globalProperties.$tabs=i;const h=n.computed(()=>!!r?.slots?.default),k=n.computed(()=>!!r?.slots?.start),s=n.computed(()=>!!r?.slots?.end),b=n.ref(0),y=n.computed(()=>{b.value;const t={};return i.tabs.forEach(o=>{const l=typeof o.title=="string"?o.title:String(o.title||fe(o));t[o.id]=l}),t});function $(){b.value++}const B=new Map,A=new Map;function U(t,o){if(!(!o||B.has(t)))try{B.set(t,o);const l=i.tabs.find(g=>i.getRouteKey(g.to)===t);if(!l){console.warn(`[RouterTab] Cannot setup watching: tab not found for ${t}`);return}const p=[];if(o.routeTabTitle!==void 0)try{const g=n.watch(()=>{const d=o.routeTabTitle;return d&&typeof d=="object"&&"value"in d?d.value:d},d=>{if(d!=null){const N=String(d);l.title=N,$()}},{immediate:!0});p.push(g)}catch(g){console.error(`[RouterTab] Error watching routeTabTitle for ${t}:`,g)}if(o.routeTabIcon!==void 0)try{const g=n.watch(()=>{const d=o.routeTabIcon;return d&&typeof d=="object"&&"value"in d?d.value:d},d=>{d!=null&&(l.icon=String(d),$())},{immediate:!0});p.push(g)}catch(g){console.error(`[RouterTab] Error watching routeTabIcon for ${t}:`,g)}if(o.routeTabClosable!==void 0)try{const g=n.watch(()=>{const d=o.routeTabClosable;return d&&typeof d=="object"&&"value"in d?d.value:d},d=>{d!=null&&(l.closable=!!d,$())},{immediate:!0});p.push(g)}catch(g){console.error(`[RouterTab] Error watching routeTabClosable for ${t}:`,g)}if(o.routeTabMeta!==void 0)try{const g=n.watch(()=>{const d=o.routeTabMeta;return d&&typeof d=="object"&&"value"in d?d.value:d},d=>{d&&typeof d=="object"&&(Object.assign(l,d),$())},{immediate:!0,deep:!0});p.push(g)}catch(g){console.error(`[RouterTab] Error watching routeTabMeta for ${t}:`,g)}A.set(t,p)}catch(l){console.error(`[RouterTab] Error in setupComponentWatching for ${t}:`,l),I(t)}}function I(t){try{const o=A.get(t);o&&(o.forEach(l=>{try{l()}catch(p){console.error(`[RouterTab] Error cleaning up watcher for ${t}:`,p)}}),A.delete(t)),B.delete(t)}catch(o){console.error(`[RouterTab] Error in cleanupComponentWatching for ${t}:`,o)}}function z(t,o){try{t?t.routeTabTitle!==void 0||t.routeTabIcon!==void 0||t.routeTabClosable!==void 0?U(o,t):t.$&&(t.$.routeTabTitle!==void 0||t.$.routeTabIcon!==void 0||t.$.routeTabClosable!==void 0)&&U(o,t.$):t===null&&I(o)}catch(l){console.error(`[RouterTab] Error handling component ref for ${o}:`,l),I(o)}}if(n.onErrorCaptured((t,o,l)=>(console.error("[RouterTab] Error captured from component:",t,l),!1)),e.cookieKey!==null||e.persistence){const t={...e.persistence??{}};e.cookieKey!==null?t.cookieKey=e.cookieKey||J:t.cookieKey||(t.cookieKey=J),ie(t)}const L=n.computed(()=>ye(e.tabTransition)),ce=n.computed(()=>ye(e.pageTransition)),w=n.reactive({visible:!1,target:null,position:{x:0,y:0}}),Q=n.ref(null),D=n.ref([]),S=n.ref(-1),Z=n.ref(null),V=new Map,C=n.reactive({dragging:!1,dragIndex:-1,dropIndex:-1,dragTab:null}),ue=["refresh","refreshAll","close","closeLefts","closeRights","closeOthers"];function _(t){return i.tabs.findIndex(o=>o.id===t)}function u(t){const o=_(t.id);return o>0?i.tabs.slice(0,o):[]}function m(t){const o=_(t.id);return o>-1?i.tabs.slice(o+1):[]}function f(t){return i.tabs.filter(o=>o.id!==t.id)}async function T(t,o){const l=t.filter(p=>p.closable!==!1);if(l.length){for(const p of l)i.activeId.value===p.id?await i.closeTab(p.id,{redirect:o.to,force:!0}):await i.removeTab(p.id,{force:!0});i.activeId.value!==o.id&&await i.openTab(o.to,!0,!1)}}const v={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})=>be(t)},closeLefts:{label:"Close to the Left",handler:async({target:t})=>{await T(u(t),t)},enable:({target:t})=>u(t).some(o=>o.closable!==!1)},closeRights:{label:"Close to the Right",handler:async({target:t})=>{await T(m(t),t)},enable:({target:t})=>m(t).some(o=>o.closable!==!1)},closeOthers:{label:"Close Others",handler:async({target:t})=>{await T(f(t),t)},enable:({target:t})=>f(t).some(o=>o.closable!==!1)}};function R(){w.visible=!1,w.target=null,S.value=-1,D.value=[]}function P(t,o){e.contextmenu&&(w.target=t,w.position.x=o.clientX,w.position.y=o.clientY,n.nextTick(()=>{w.visible=!0,document.addEventListener("click",R,{once:!0}),n.nextTick(()=>{ft()})}))}function ut(t,o){const l=typeof t=="string"?{id:t}:t,p=v[l.id],g=l.label??p?.label??String(l.id),d=l.visible??p?.visible??!0;if(!(typeof d=="function"?d(o):d!==!1))return null;const ge=l.enable??p?.enable??!0,Dt=typeof ge=="function"?ge(o):ge!==!1,Ie=l.handler??p?.handler;if(!Ie)return null;const xt=async()=>{await Promise.resolve(Ie(o))};return{id:String(l.id),label:g,disabled:!Dt,action:xt}}const F=n.computed(()=>{if(!w.visible||!w.target||e.contextmenu===!1)return[];const t=Array.isArray(e.contextmenu)?e.contextmenu:ue,o={target:w.target,controller:i};return t.map(l=>ut(l,o)).filter(l=>!!l)});function ft(){const t=Q.value;if(!t)return;const o=8,{innerWidth:l,innerHeight:p}=window,g=t.getBoundingClientRect();let d=w.position.x,N=w.position.y;g.right>l-o&&(d=Math.max(o,l-g.width-o)),g.bottom>p-o&&(N=Math.max(o,p-g.height-o)),(d!==w.position.x||N!==w.position.y)&&(w.position.x=d,w.position.y=N)}function dt(t,o){D.value[o]=t??null}function bt(t){if(t<0)return;D.value[t]?.focus({preventScroll:!0})}function ee(t,o,l=F.value){if(!l.length)return-1;const p=l.length;let g=t;for(let d=0;d<p;d++)if(g=(g+o+p)%p,!l[g].disabled)return g;return-1}function Y(t){S.value=t,!(t<0)&&n.nextTick(()=>bt(t))}function $e(t){const o=ee(S.value,t);o!==-1&&Y(o)}function mt(t){if(!w.visible)return;const o=t.key,l=F.value;if(!l.length)return;if(o==="Tab"){R();return}if(["ArrowDown","ArrowUp","ArrowRight","ArrowLeft","Home","End","Enter"," ","Spacebar","Escape"].includes(o))switch(t.preventDefault(),o){case"ArrowDown":case"ArrowRight":$e(1);break;case"ArrowUp":case"ArrowLeft":$e(-1);break;case"Home":Y(ee(-1,1));break;case"End":Y(ee(l.length,-1));break;case"Enter":case" ":case"Spacebar":{const g=S.value;if(g>-1){const d=l[g];d.disabled||Be(d)}break}case"Escape":R();break}}async function Be(t){t.disabled||(R(),await t.action())}function fe(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 pt(t){return y.value[t.id]||fe(t)}function Pe(t,o){return n.defineComponent({name:o,setup(l,{attrs:p,slots:g}){return()=>n.h(t,p,g)}})}const H=new Map,de=n.ref(0);function gt(t,o,l){H.has(l)||(H.set(l,o),de.value++),t&&z(t,l)}function ht(t,o){return t&&((!t.name||t.name!==o)&&(t.name=o),t)}function yt(t,o){if(!t)return t;const l=H.get(o);if(l)return l;const p=Pe(t,o);return H.set(o,p),de.value++,p}function Tt(t){const o=i.getRouteKey(t),l=c.currentRoute.value,p=i.getRouteKey(l);return o===p}function Se(t){const o=i.getRouteKey(t),l=i.tabs.find(d=>d.id===o);if(!l)return console.warn("[RouterTab] Tab not found for route:",o),`${o}::0`;const p=l.renderKey??0,g=`${o}::${p}`;return(o.includes("students")||o.includes("classroom")||o.includes("quiz"))&&console.log(`[getComponentCacheKey] Route: ${t.fullPath}`,{routeKey:o,renderKey:p,cacheKey:g,tabAlive:l.alive,includeKeys:pe.value,isIncluded:pe.value.includes(g)}),g}function vt(t){return`${Se(t)}::refresh`}function be(t){return!(t.closable===!1||i.options.keepLastTab&&i.tabs.length<=1)}async function kt(t){await i.closeTab(t.id)}function wt(t,o){o?V.set(t,o):V.delete(t)}function me(t){n.nextTick(()=>{const o=V.get(t),l=Z.value;if(o&&l){const p=o.getBoundingClientRect(),g=l.getBoundingClientRect();(p.left<g.left||p.right>g.right)&&o.scrollIntoView({behavior:"smooth",block:"nearest",inline:"nearest"})}})}function Rt(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),me(t.id))}function Ct(t){return["router-tab__item",{"is-active":i.activeId.value===t.id,"is-closable":be(t),"is-dragging":C.dragging&&C.dragTab?.id===t.id,"is-drag-over":C.dropIndex===_(t.id)},t.tabClass]}function Et(t){return i.refreshingKey.value===i.getRouteKey(t)}function Kt(t){const o=i.getRouteKey(t),l=i.tabs.find(p=>p.id===o);return l?l.alive:!1}function $t(t){const o=i.getRouteKey(t);return!!i.tabs.find(p=>p.id===o)}function Bt(t,o,l){e.sortable&&(C.dragging=!0,C.dragIndex=o,C.dragTab=t,l.dataTransfer&&(l.dataTransfer.effectAllowed="move",l.dataTransfer.setData("text/plain",t.id)),a("tab-sort",{tab:t,index:o}))}function Pt(t,o){!e.sortable||!C.dragging||(o.preventDefault(),o.dataTransfer&&(o.dataTransfer.dropEffect="move"))}function St(t){!e.sortable||!C.dragging||(C.dropIndex=t)}function At(){!e.sortable||C.dragging}function It(t,o){if(!(!e.sortable||!C.dragging)){if(o.preventDefault(),C.dragIndex!==-1&&C.dragIndex!==t){const l=i.tabs.splice(C.dragIndex,1)[0];i.tabs.splice(t,0,l),a("tab-sorted",{tab:l,fromIndex:C.dragIndex,toIndex:t})}Ae()}}function Ae(){C.dragging=!1,C.dragIndex=-1,C.dropIndex=-1,C.dragTab=null}n.onMounted(()=>{document.addEventListener("keydown",R)}),n.onBeforeUnmount(()=>{document.removeEventListener("keydown",R),r.appContext.config.globalProperties.$tabs=null,A.forEach(t=>{t.forEach(o=>{try{o()}catch(l){console.error("[RouterTab] Error during cleanup:",l)}})}),A.clear(),B.clear()}),n.watch(()=>e.keepAlive,t=>{i.options.keepAlive=t}),n.watch(()=>i.activeId.value,t=>{t&&me(t),R()}),n.watch(()=>i.tabs.length,()=>{const t=new Set(i.tabs.map(l=>l.id));Array.from(B.keys()).forEach(l=>{t.has(l)||(console.log(`[RouterTab] Cleaning up stale component instance: ${l}`),I(l))})}),n.watch(()=>e.contextmenu,t=>{t||R()}),n.watch(()=>F.value.length,t=>{w.visible&&t===0&&R()},{flush:"post"}),n.watch(F,t=>{if(!w.visible)return;D.value=new Array(t.length).fill(null);const o=ee(-1,1,t);Y(o)},{flush:"post"}),n.watch(()=>w.visible,t=>{t||(S.value=-1,D.value=[])});const pe=i.includeKeys;return{controller:i,tabs:i.tabs,includeKeys:pe,componentCache:H,componentCacheTrigger:de,cacheCurrentComponent:gt,tabTransitionProps:L,pageTransitionProps:ce,buildTabClass:Ct,activate:Rt,close:kt,context:w,menuItems:F,handleMenuAction:Be,showContextMenu:P,hideContextMenu:R,getTabTitle:fe,isClosable:be,isRefreshing:Et,isTabCached:Kt,isTabReady:$t,hasCustomSlot:h,hasStartSlot:k,hasEndSlot:s,onDragStart:Bt,onDragOver:Pt,onDragEnter:St,onDragLeave:At,onDrop:It,onDragEnd:Ae,setupComponentWatching:U,cleanupComponentWatching:I,handleComponentRef:z,getReactiveTabTitle:pt,getComponentCacheKey:Se,getRefreshComponentKey:vt,createNamedComponent:Pe,ensureNamedComponent:ht,getNamedComponent:yt,shouldRenderRoute:Tt,triggerTabUpdate:$,menuRef:Q,highlightedIndex:S,setMenuItemRef:dt,onMenuKeydown:mt,highlightMenuIndex:Y,scrollContainer:Z,setTabRef:wt,scrollTabIntoView:me}}}),Fe=(e,a)=>{const r=e.__vccOpts||e;for(const[c,i]of a)r[c]=i;return r},Ye={class:"router-tab"},He={class:"router-tab__header"},We={class:"router-tab__scroll",ref:"scrollContainer"},Je=["data-title","draggable","onClick","onAuxclick","onContextmenu","onDragstart","onDragover","onDragenter","onDrop"],qe=["title"],Ge=["onClick"],Xe={class:"router-tab__container"},Qe=["aria-disabled","disabled","tabindex","onMouseenter","onClick"];function Ze(e,a,r,c,i,h){const k=n.resolveComponent("RouterView");return n.openBlock(),n.createElementBlock("div",Ye,[n.createElementVNode("header",He,[n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-start",{"has-content":e.hasStartSlot}])},[n.renderSlot(e.$slots,"start")],2),n.createElementVNode("div",We,[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,(s,b)=>(n.openBlock(),n.createElementBlock("li",{key:s.id,class:n.normalizeClass(e.buildTabClass(s)),"data-title":e.getTabTitle(s),draggable:e.sortable,ref_for:!0,ref:y=>e.setTabRef(s.id,y),onClick:y=>e.activate(s),onAuxclick:n.withModifiers(y=>e.close(s),["middle","prevent"]),onContextmenu:n.withModifiers(y=>e.showContextMenu(s,y),["prevent"]),onDragstart:y=>e.onDragStart(s,b,y),onDragover:y=>e.onDragOver(b,y),onDragenter:y=>e.onDragEnter(b),onDragleave:a[0]||(a[0]=(...y)=>e.onDragLeave&&e.onDragLeave(...y)),onDrop:y=>e.onDrop(b,y),onDragend:a[1]||(a[1]=(...y)=>e.onDragEnd&&e.onDragEnd(...y))},[s.icon?(n.openBlock(),n.createElementBlock("i",{key:0,class:n.normalizeClass(["router-tab__item-icon",s.icon])},null,2)):n.createCommentVNode("",!0),n.createElementVNode("span",{class:"router-tab__item-title",title:e.getReactiveTabTitle(s)},n.toDisplayString(e.getReactiveTabTitle(s)),9,qe),e.isClosable(s)?(n.openBlock(),n.createElementBlock("a",{key:1,class:"router-tab__item-close",onClick:n.withModifiers(y=>e.close(s),["stop"])},null,8,Ge)):n.createCommentVNode("",!0)],42,Je))),128))]),_:1},16)],512),n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-end",{"has-content":e.hasEndSlot}])},[n.renderSlot(e.$slots,"end")],2)]),n.createElementVNode("div",Xe,[n.createVNode(k,null,{default:n.withCtx(({Component:s,route:b})=>[e.hasCustomSlot?n.renderSlot(e.$slots,"default",n.normalizeProps(n.mergeProps({key:0},{Component:s,route:b,controller:e.controller,pageRef:y=>e.handleComponentRef(y,e.controller.getRouteKey(b))}))):(n.openBlock(),n.createElementBlock(n.Fragment,{key:1},[e.controller.options.keepAlive?(n.openBlock(),n.createBlock(n.KeepAlive,{key:0,include:e.includeKeys},[s?(n.openBlock(),n.createBlock(n.resolveDynamicComponent(e.getNamedComponent(s,e.getComponentCacheKey(b))),{key:e.isRefreshing(b)?e.getRefreshComponentKey(b):e.getComponentCacheKey(b),ref:y=>e.handleComponentRef(y,e.controller.getRouteKey(b)),class:"router-tab-page"})):n.createCommentVNode("",!0)],1032,["include"])):(n.openBlock(),n.createBlock(n.Transition,n.mergeProps({key:1},e.pageTransitionProps,{appear:""}),{default:n.withCtx(()=>[s?(n.openBlock(),n.createBlock(n.resolveDynamicComponent(s),{key:e.controller.getRouteKey(b),ref:y=>e.handleComponentRef(y,e.controller.getRouteKey(b)),class:"router-tab-page"})):n.createCommentVNode("",!0)]),_:2},1040))],64))]),_:3})]),n.withDirectives(n.createElementVNode("div",{ref:"menuRef",class:"router-tab__contextmenu",role:"menu",onKeydown:a[2]||(a[2]=(...s)=>e.onMenuKeydown&&e.onMenuKeydown(...s)),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,(s,b)=>(n.openBlock(),n.createElementBlock("a",{key:s.id,role:"menuitem",class:n.normalizeClass(["router-tab__contextmenu-item",{"is-focused":b===e.highlightedIndex}]),"aria-disabled":s.disabled,disabled:s.disabled,tabindex:s.disabled?-1:b===e.highlightedIndex?0:-1,ref_for:!0,ref:y=>e.setMenuItemRef(y,b),onMouseenter:y=>!s.disabled&&e.highlightMenuIndex(b),onClick:y=>e.handleMenuAction(s)},n.toDisplayString(s.label),43,Qe))),128))],36),[[n.vShow,e.context.visible&&e.context.target]])])}const le=Fe(_e,[["render",Ze]]),et={class:"router-tabs","aria-hidden":"true"},q=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 ie(e),(r,c)=>(n.openBlock(),n.createElementBlock("span",et))}}),ke="tab-theme-style",we="tab-theme-primary-color",tt="system",Re="(prefers-color-scheme: dark)";let j=null;const K={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"},nt={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 ot(e){if(typeof window>"u")return null;const a=window.localStorage.getItem(e);if(!a)return null;try{const r=JSON.parse(a);return r&&typeof r=="object"?r:null}catch{return null}}function se(e){typeof document>"u"||(document.documentElement.style.setProperty("--router-tab-primary",e.primary??K.primary),document.documentElement.style.setProperty("--router-tab-header-bg",e.headerBackground??K.headerBackground),document.documentElement.style.setProperty("--router-tab-background",e.background??K.background),document.documentElement.style.setProperty("--router-tab-active-background",e.activeBackground??K.activeBackground),document.documentElement.style.setProperty("--router-tab-text",e.text??K.text),document.documentElement.style.setProperty("--router-tab-active-text",e.activeText??K.activeText),document.documentElement.style.setProperty("--router-tab-border",e.border??K.border),document.documentElement.style.setProperty("--router-tab-active-border",e.activeBorder??K.activeBorder),document.documentElement.style.setProperty("--router-tab-button-color",e.buttonColor??K.buttonColor),document.documentElement.style.setProperty("--router-tab-active-button-color",e.activeButtonColor??K.activeButtonColor),document.documentElement.style.setProperty("--router-tab-button-background",e.buttonBackground??K.buttonBackground),document.documentElement.style.setProperty("--router-tab-active-button-background",e.activeButtonBackground??K.activeButtonBackground),document.documentElement.style.setProperty("--router-tab-icon-color",e.iconColor??K.iconColor))}function Ce(e){if(typeof document>"u")return;const a=document.documentElement,r=window.matchMedia(Re),c=()=>{a.dataset.theme=r.matches?"dark":"light"};j&&(r.removeEventListener("change",j),j=null),e==="system"?(c(),j=()=>c(),r.addEventListener("change",j)):a.dataset.theme=e}function Ee(e={}){if(typeof window>"u")return;const{styleKey:a=ke,primaryKey:r=we,defaultStyle:c=tt,defaultPrimary:i}=e,h=window.localStorage.getItem(a)??c;Ce(h);const s=h==="dark"||h==="system"&&window.matchMedia(Re).matches?{...nt}:{...K};i&&(s.primary=i);const b=ot(r);se(b?{...s,...b}:s)}function rt(e,a){if(typeof window>"u")return;const r=a?.styleKey??ke;window.localStorage.setItem(r,e),Ce(e)}function at(e,a){if(typeof window>"u")return;const r=a?.primaryKey??we;window.localStorage.setItem(r,JSON.stringify(e)),se(e)}function G(e,a){if(n.isRef(e)){const c=!n.isReadonly(e);return{value:e,update:c?i=>{e.value=i}:()=>{}}}if(typeof e=="function"){const c=e;return{value:n.computed(c),update:()=>{}}}const r=n.ref(e===void 0?a:e);return{value:r,update:c=>{r.value=c}}}function X(e={}){const a=G(e.title,"Untitled"),r=G(e.icon,""),c=G(e.closable,!0),i=G(e.meta,{});return{routeTabTitle:a.value,routeTabIcon:r.value,routeTabClosable:c.value,routeTabMeta:i.value,updateTitle:a.update,updateIcon:r.update,updateClosable:c.update,updateMeta:i.update}}function it(e,a="Page"){return X({title:n.computed(()=>e.value?"Loading...":a),icon:n.computed(()=>e.value?"mdi-loading mdi-spin":"mdi-page"),closable:n.computed(()=>!e.value)})}function lt(e,a="Page",r="mdi-page"){return X({title:n.computed(()=>e.value>0?`${a} (${e.value})`:a),icon:n.computed(()=>e.value>0?"mdi-bell-badge":r)})}function st(e,a="Page"){const r={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 X({title:n.computed(()=>a+r[e.value].suffix),icon:n.computed(()=>r[e.value].icon),closable:n.computed(()=>e.value!=="loading")})}let Ke=!1;const ct={install(e,a){if(Ke)return;Ke=!0;const{initTheme:r=!0,themeOptions:c,componentName:i=le.name||"RouterTab",tabsComponentName:h=q.name||"RouterTabs"}=a??{};r&&Ee(c??{}),e.component(i,le),e.component(h,q),h.toLowerCase()!=="router-tabs"&&e.component("router-tabs",q),Object.defineProperty(e.config.globalProperties,"$tabs",{configurable:!0,enumerable:!1,get(){return e._context.provides[O]},set(k){k&&e.provide(O,k)}})}};E.RouterTab=le,E.RouterTabs=q,E.default=ct,E.initRouterTabsTheme=Ee,E.routerTabsKey=O,E.setRouterTabsPrimary=at,E.setRouterTabsTheme=rt,E.useLoadingTab=it,E.useNotificationTab=lt,E.useReactiveTab=X,E.useRouterTabs=ae,E.useRouterTabsPersistence=ie,E.useStatusTab=st,Object.defineProperties(E,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
1
+ (function(E,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):(E=typeof globalThis<"u"?globalThis:E||self,n(E["vue3-router-tab"]={},E.Vue,E.VueRouter))})(this,(function(E,n,Ie){"use strict";function xe(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 I(e,a){const r=e.resolve(a);if(!r||!r.matched.length)throw new Error(`[RouterTabs] Unable to resolve route: ${String(a)}`);return r}const De={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 V(e){const a=e.meta?.key;if(typeof a=="function"){const r=a(e);if(typeof r=="string"&&r.length)return r}else if(typeof a=="string"&&a.length){const r=De[a.toLowerCase()];return r?r(e):a}return e.fullPath}function te(e,a){const r=e.meta?.keepAlive;return typeof r=="boolean"?r:a}function ne(e,a){const r=e.meta?.reuse;return typeof r=="boolean"?r:a}function he(e){const a=e.meta??{},r={};return"title"in a&&(r.title=a.title),"tips"in a&&(r.tips=a.tips),"icon"in a&&(r.icon=a.icon),"closable"in a&&(r.closable=a.closable),"tabClass"in a&&(r.tabClass=a.tabClass),"target"in a&&(r.target=a.target),"href"in a&&(r.href=a.href),r}function W(e,a,r){const c=he(e);return{id:V(e),to:e.fullPath,fullPath:e.fullPath,matched:e,alive:te(e,r),reusable:ne(e,!1),closable:c.closable??!0,renderKey:typeof a.renderKey=="number"?a.renderKey:0,...c,...a}}function oe(e,a,r,c){if(!e.find(g=>g.id===a.id)){if(r==="next"&&c){const g=e.findIndex(w=>w.id===c);if(g!==-1){e.splice(g+1,0,a);return}}e.push(a)}}function J(e,a,r,c){if(!a||a<=0)return;const i=e.filter(g=>g.alive);for(;i.length>a;){const g=i.shift();if(!g||g.id===r)continue;const w=e.findIndex(l=>l.id===g.id);if(w>-1){const l=e[w],y=`${l.id}::${l.renderKey??0}`;c.delete(y),l.alive=!1}}}function Me(e){return{to:e.to,title:e.title,tips:e.tips,icon:e.icon,tabClass:e.tabClass,closable:e.closable,renderKey:e.renderKey}}function Le(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),"renderKey"in e&&typeof e.renderKey=="number"&&(a.renderKey=e.renderKey),a}function Ve(e,a={}){const r=xe(a),c=n.reactive([]),i=n.ref(null),g=n.shallowRef(),w=n.ref(null),l=n.reactive(new Set),y=n.computed(()=>Array.from(l));let h=!1;function v(u){const b=typeof u.matched=="object"?u:I(e,u);return{key:V(b),fullPath:b.fullPath,alive:te(b,r.keepAlive),reusable:ne(b,!1),matched:b}}function S(u){const b=V(u);let d=c.find(k=>k.id===b);const T=te(u,r.keepAlive);if(d){d.fullPath=u.fullPath,d.to=u.fullPath,d.matched=u,d.reusable=ne(u,d.reusable),typeof d.renderKey!="number"&&(d.renderKey=0);const k=`${b}::${d.renderKey}`;return T?l.has(k)?d.alive||(d.alive=!0):(l.add(k),d.alive=!0):d.alive&&(l.delete(k),d.alive=!1),Object.assign(d,he(u)),d}if(d=W(u,{},r.keepAlive),d.alive){const k=`${b}::${d.renderKey??0}`;l.add(k)}return oe(c,d,r.appendPosition,i.value),J(c,r.maxAlive,i.value,l),d}async function $(u,b=!1,d="sameTab"){const T=I(e,u),k=V(T),B=i.value===k;d==="sameTab"&&(d=B),d&&await M(k,!0),await e[b?"replace":"push"](T),B&&await U()}function x(u){const b=c.findIndex(K=>K.id===u);if(b===-1)return r.defaultRoute;const d=c[b+1],T=c[b-1],k=c.find(K=>K.id!==u),B=d||T||k;return B?B.to:r.defaultRoute}async function D(u=i.value,b={}){if(!u)return;if(!b.force&&r.keepLastTab&&c.length===1)throw new Error("[RouterTabs] Unable to close the final tab when keepLastTab is true.");const T=i.value===u&&b.redirect!==null,k=T?b.redirect??x(u):null;await _(u,{force:b.force}),b.redirect!==null&&T&&k&&await e.replace(k)}async function _(u,b={}){const d=c.findIndex(B=>B.id===u);if(d===-1)return;const T=c[d],k=`${u}::${T.renderKey??0}`;l.delete(k),T.alive=!1,c.splice(d,1),w.value===u&&(w.value=null),i.value===u&&(i.value=null,g.value=void 0)}async function M(u=i.value??void 0,b=!1){if(!u)return;const d=c.find(K=>K.id===u);if(!d)return;const T=r.keepAlive&&d.alive,k=`${u}::${d.renderKey??0}`;T&&(l.delete(k),d.alive=!1,await n.nextTick()),d.renderKey=(d.renderKey??0)+1;const B=`${u}::${d.renderKey}`;T&&(l.add(B),d.alive=!0),w.value=u,await n.nextTick(),b||await n.nextTick(),w.value=null}async function se(u=!1){for(const b of c)await M(b.id,u)}function ce(u,b){const d=c.find(k=>k.id===u);if(!d)return;const T=`${u}::${d.renderKey??0}`;b?(l.add(T),d.alive=!0,J(c,r.maxAlive,i.value,l)):(l.delete(T),d.alive=!1)}function C(u){const b=c.find(T=>T.id===u);if(!b)return;const d=`${u}::${b.renderKey??0}`;l.delete(d),b.alive=!1,b.renderKey=(b.renderKey??0)+1}function Z(){l.clear(),c.forEach(u=>{u.alive=!1})}function L(){return y.value.slice()}async function A(u=r.defaultRoute){c.splice(0,c.length),i.value=null,g.value=void 0;for(const b of r.initialTabs){const d=I(e,b.to),T=W(d,b,r.keepAlive);c.push(T)}await e.replace(u)}async function U(){const u=i.value;u&&await M(u,!0)}function z(u){return typeof u.matched=="object"?V(u):V(I(e,u))}function R(){const u=c.find(b=>b.id===i.value);return{tabs:c.map(Me),active:u?u.to:null}}async function ue(u){h=!0,c.splice(0,c.length),i.value=null,g.value=void 0;const b=u?.tabs??[];for(const T of b)try{const k=I(e,T.to),B=Le(T),K=W(k,B,r.keepAlive);oe(c,K,"last",null)}catch(k){console.warn("[RouterTabs] Failed to restore tab",T,k)}h=!1;const d=u?.active??b[b.length-1]?.to??r.defaultRoute;if(d)try{const T=I(e,d),k=e.currentRoute.value;if(T.fullPath===k.fullPath){const B=S(k);i.value=B.id,g.value=B,J(c,r.maxAlive,i.value,l);return}await e.replace(T)}catch(T){console.warn("[RouterTabs] Failed to navigate to restored route",d,T)}}return n.watch(()=>e.currentRoute.value,u=>{if(h)return;const b=S(u);i.value=b.id,g.value=b,J(c,r.maxAlive,i.value,l)},{immediate:!0}),r.initialTabs.length&&r.initialTabs.forEach(u=>{const b=I(e,u.to),d=W(b,u,r.keepAlive);oe(c,d,"last",null)}),{options:r,tabs:c,activeId:i,current:g,includeKeys:y,refreshingKey:w,openTab:$,closeTab:D,removeTab:_,refreshTab:M,refreshAll:se,setTabAlive:ce,evictCache:C,clearCache:Z,getCacheKeys:L,reset:A,reload:U,getRouteKey:z,matchRoute:v,snapshot:R,hydrate:ue,ensureTab:S}}function ye(e){return e?typeof e=="string"?{name:e}:e:{}}const O=Symbol("RouterTabsContext"),q="router-tabs:snapshot";function re(e={}){const{optional:a=!1}=e,r=n.inject(O,null);if(r)return r;const c=n.inject("$tabs",null);if(c)return c;const g=n.getCurrentInstance()?.appContext.config.globalProperties.$tabs;if(g)return g;if(!a)throw new Error("[RouterTabs] useRouterTabs must be used within <router-tab>.");return null}const Ne=864e5;function Oe(e){if(typeof document>"u")return null;const a=`${encodeURIComponent(e)}=`,r=document.cookie?document.cookie.split("; "):[];for(const c of r)if(c.startsWith(a))return decodeURIComponent(c.slice(a.length));return null}function Te(e,a,r){if(typeof document>"u")return;const{expiresInDays:c=7,path:i="/",domain:g,secure:w,sameSite:l="lax"}=r,y=[`${encodeURIComponent(e)}=${encodeURIComponent(a)}`];if(c!==1/0){const h=new Date(Date.now()+c*Ne).toUTCString();y.push(`Expires=${h}`)}i&&y.push(`Path=${i}`),g&&y.push(`Domain=${g}`),w&&y.push("Secure"),l&&y.push(`SameSite=${l.charAt(0).toUpperCase()}${l.slice(1)}`),document.cookie=y.join("; ")}function ke(e,a){if(typeof document>"u")return;const{path:r="/",domain:c}=a,i=[`${encodeURIComponent(e)}=`];i.push("Expires=Thu, 01 Jan 1970 00:00:01 GMT"),r&&i.push(`Path=${r}`),c&&i.push(`Domain=${c}`),document.cookie=i.join("; ")}const je=e=>JSON.stringify(e??null),_e=e=>{if(!e)return null;try{return JSON.parse(e)}catch{return null}};function ae(e={}){const{cookieKey:a=q,serialize:r=je,deserialize:c=_e}=e,i=re({optional:!0}),g=n.ref(!0),w=(l,y="hook")=>{const h=async()=>{g.value=!0;try{const v=c(Oe(a));if(v&&v.tabs?.length){if(await l.hydrate(v),v.active){await n.nextTick();const $=l.tabs.find(x=>x.to===v.active);$&&(l.activeId.value=$.id,l.current.value=$)}}else if(Object.prototype.hasOwnProperty.call(e,"fallbackRoute")){const $=e.fallbackRoute??l.options.defaultRoute;await l.reset($)}const S=l.snapshot();S.tabs.length?Te(a,r(S),e):ke(a,e)}finally{g.value=!1}};y==="immediate"?h():n.onBeforeMount(()=>{h()}),n.watch(()=>({tabs:l.tabs.map(v=>({to:v.to,title:v.title,tips:v.tips,icon:v.icon,tabClass:v.tabClass,closable:v.closable,renderKey:v.renderKey})),active:l.activeId.value}),()=>{if(g.value)return;const v=l.snapshot();v.tabs.length?Te(a,r(v),e):ke(a,e)},{deep:!0})};return i?w(i):n.onMounted(()=>{const l=re({optional:!0});l&&w(l,"immediate")}),{hydrating:g}}const Ue=n.defineComponent({name:"RouterTab",components:{RouterView:Ie.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:q},persistence:{type:Object,default:null},sortable:{type:Boolean,default:!0}},emits:["tab-sort","tab-sorted"],setup(e,{emit:a}){const r=n.getCurrentInstance();if(!r)throw new Error("[RouterTab] component must be used within a Vue application context.");const c=r.appContext.app.config.globalProperties.$router;if(!c)throw new Error("[RouterTab] Vue Router is required. Make sure to call app.use(router) before RouterTab.");const i=Ve(c,{initialTabs:e.tabs,keepAlive:e.keepAlive,maxAlive:e.maxAlive,keepLastTab:e.keepLastTab,appendPosition:e.append,defaultRoute:e.defaultPage});n.provide(O,i),r.appContext.config.globalProperties.$tabs=i;const g=n.computed(()=>!!r?.slots?.default),w=n.computed(()=>!!r?.slots?.start),l=n.computed(()=>!!r?.slots?.end),y=n.ref(0),h=n.computed(()=>{y.value;const t={};return i.tabs.forEach(o=>{const s=typeof o.title=="string"?o.title:String(o.title||de(o));t[o.id]=s}),t});function v(){y.value++}const S=new Map,$=new Map;function x(t,o){if(!(!o||S.has(t)))try{S.set(t,o);const s=i.tabs.find(p=>i.getRouteKey(p.to)===t);if(!s){console.warn(`[RouterTab] Cannot setup watching: tab not found for ${t}`);return}const m=[];if(o.routeTabTitle!==void 0)try{const p=n.watch(()=>{const f=o.routeTabTitle;return f&&typeof f=="object"&&"value"in f?f.value:f},f=>{if(f!=null){const N=String(f);s.title=N,v()}},{immediate:!0});m.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabTitle for ${t}:`,p)}if(o.routeTabIcon!==void 0)try{const p=n.watch(()=>{const f=o.routeTabIcon;return f&&typeof f=="object"&&"value"in f?f.value:f},f=>{f!=null&&(s.icon=String(f),v())},{immediate:!0});m.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabIcon for ${t}:`,p)}if(o.routeTabClosable!==void 0)try{const p=n.watch(()=>{const f=o.routeTabClosable;return f&&typeof f=="object"&&"value"in f?f.value:f},f=>{f!=null&&(s.closable=!!f,v())},{immediate:!0});m.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabClosable for ${t}:`,p)}if(o.routeTabMeta!==void 0)try{const p=n.watch(()=>{const f=o.routeTabMeta;return f&&typeof f=="object"&&"value"in f?f.value:f},f=>{f&&typeof f=="object"&&(Object.assign(s,f),v())},{immediate:!0,deep:!0});m.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabMeta for ${t}:`,p)}$.set(t,m)}catch(s){console.error(`[RouterTab] Error in setupComponentWatching for ${t}:`,s),D(t)}}function D(t){try{const o=$.get(t);o&&(o.forEach(s=>{try{s()}catch(m){console.error(`[RouterTab] Error cleaning up watcher for ${t}:`,m)}}),$.delete(t)),S.delete(t)}catch(o){console.error(`[RouterTab] Error in cleanupComponentWatching for ${t}:`,o)}}function _(t,o){try{t?t.routeTabTitle!==void 0||t.routeTabIcon!==void 0||t.routeTabClosable!==void 0?x(o,t):t.$&&(t.$.routeTabTitle!==void 0||t.$.routeTabIcon!==void 0||t.$.routeTabClosable!==void 0)&&x(o,t.$):t===null&&D(o)}catch(s){console.error(`[RouterTab] Error handling component ref for ${o}:`,s),D(o)}}n.onErrorCaptured((t,o,s)=>(console.error("[RouterTab] Error captured from component:",t,s),!1));let M=n.ref(!1);if(e.cookieKey!==null||e.persistence){const t={...e.persistence??{}};e.cookieKey!==null?t.cookieKey=e.cookieKey||q:t.cookieKey||(t.cookieKey=q),M=ae(t).hydrating}const se=n.computed(()=>ye(e.tabTransition)),ce=n.computed(()=>ye(e.pageTransition)),C=n.reactive({visible:!1,target:null,position:{x:0,y:0}}),Z=n.ref(null),L=n.ref([]),A=n.ref(-1),U=n.ref(null),z=new Map,R=n.reactive({dragging:!1,dragIndex:-1,dropIndex:-1,dragTab:null}),ue=["refresh","refreshAll","close","closeLefts","closeRights","closeOthers"];function u(t){return i.tabs.findIndex(o=>o.id===t)}function b(t){const o=u(t.id);return o>0?i.tabs.slice(0,o):[]}function d(t){const o=u(t.id);return o>-1?i.tabs.slice(o+1):[]}function T(t){return i.tabs.filter(o=>o.id!==t.id)}async function k(t,o){const s=t.filter(m=>m.closable!==!1);if(s.length){for(const m of s)i.activeId.value===m.id?await i.closeTab(m.id,{redirect:o.to,force:!0}):await i.removeTab(m.id,{force:!0});i.activeId.value!==o.id&&await i.openTab(o.to,!0,!1)}}const B={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})=>be(t)},closeLefts:{label:"Close to the Left",handler:async({target:t})=>{await k(b(t),t)},enable:({target:t})=>b(t).some(o=>o.closable!==!1)},closeRights:{label:"Close to the Right",handler:async({target:t})=>{await k(d(t),t)},enable:({target:t})=>d(t).some(o=>o.closable!==!1)},closeOthers:{label:"Close Others",handler:async({target:t})=>{await k(T(t),t)},enable:({target:t})=>T(t).some(o=>o.closable!==!1)}};function K(){C.visible=!1,C.target=null,A.value=-1,L.value=[]}function ut(t,o){e.contextmenu&&(C.target=t,C.position.x=o.clientX,C.position.y=o.clientY,n.nextTick(()=>{C.visible=!0,document.addEventListener("click",K,{once:!0}),n.nextTick(()=>{ft()})}))}function dt(t,o){const s=typeof t=="string"?{id:t}:t,m=B[s.id],p=s.label??m?.label??String(s.id),f=s.visible??m?.visible??!0;if(!(typeof f=="function"?f(o):f!==!1))return null;const ge=s.enable??m?.enable??!0,xt=typeof ge=="function"?ge(o):ge!==!1,Ae=s.handler??m?.handler;if(!Ae)return null;const Dt=async()=>{await Promise.resolve(Ae(o))};return{id:String(s.id),label:p,disabled:!xt,action:Dt}}const F=n.computed(()=>{if(!C.visible||!C.target||e.contextmenu===!1)return[];const t=Array.isArray(e.contextmenu)?e.contextmenu:ue,o={target:C.target,controller:i};return t.map(s=>dt(s,o)).filter(s=>!!s)});function ft(){const t=Z.value;if(!t)return;const o=8,{innerWidth:s,innerHeight:m}=window,p=t.getBoundingClientRect();let f=C.position.x,N=C.position.y;p.right>s-o&&(f=Math.max(o,s-p.width-o)),p.bottom>m-o&&(N=Math.max(o,m-p.height-o)),(f!==C.position.x||N!==C.position.y)&&(C.position.x=f,C.position.y=N)}function bt(t,o){L.value[o]=t??null}function mt(t){if(t<0)return;L.value[t]?.focus({preventScroll:!0})}function ee(t,o,s=F.value){if(!s.length)return-1;const m=s.length;let p=t;for(let f=0;f<m;f++)if(p=(p+o+m)%m,!s[p].disabled)return p;return-1}function Y(t){A.value=t,!(t<0)&&n.nextTick(()=>mt(t))}function Be(t){const o=ee(A.value,t);o!==-1&&Y(o)}function pt(t){if(!C.visible)return;const o=t.key,s=F.value;if(!s.length)return;if(o==="Tab"){K();return}if(["ArrowDown","ArrowUp","ArrowRight","ArrowLeft","Home","End","Enter"," ","Spacebar","Escape"].includes(o))switch(t.preventDefault(),o){case"ArrowDown":case"ArrowRight":Be(1);break;case"ArrowUp":case"ArrowLeft":Be(-1);break;case"Home":Y(ee(-1,1));break;case"End":Y(ee(s.length,-1));break;case"Enter":case" ":case"Spacebar":{const p=A.value;if(p>-1){const f=s[p];f.disabled||Pe(f)}break}case"Escape":K();break}}async function Pe(t){t.disabled||(K(),await t.action())}function de(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 gt(t){return h.value[t.id]||de(t)}function $e(t,o){return n.defineComponent({name:o,setup(s,{attrs:m,slots:p}){return()=>n.h(t,m,p)}})}const H=new Map,fe=n.ref(0);function ht(t,o,s){H.has(s)||(H.set(s,o),fe.value++),t&&_(t,s)}function yt(t,o){return t&&((!t.name||t.name!==o)&&(t.name=o),t)}function Tt(t,o){if(!t)return t;const s=H.get(o);if(s)return s;const m=$e(t,o);return H.set(o,m),fe.value++,m}function kt(t){const o=i.getRouteKey(t),s=c.currentRoute.value,m=i.getRouteKey(s);return o===m}function vt(t){const o=i.getRouteKey(t),s=i.tabs.find(f=>f.id===o);if(!s)return console.warn("[RouterTab] Tab not found for route:",o),`${o}::0`;const m=s.renderKey??0,p=`${o}::${m}`;return(o.includes("students")||o.includes("classroom")||o.includes("quiz"))&&console.log(`[getComponentCacheKey] Route: ${t.fullPath}`,{routeKey:o,renderKey:m,cacheKey:p,tabAlive:s.alive,includeKeys:pe.value,isIncluded:pe.value.includes(p)}),p}function be(t){return!(t.closable===!1||i.options.keepLastTab&&i.tabs.length<=1)}async function wt(t){await i.closeTab(t.id)}function Ct(t,o){o?z.set(t,o):z.delete(t)}function me(t){n.nextTick(()=>{const o=z.get(t),s=U.value;if(o&&s){const m=o.getBoundingClientRect(),p=s.getBoundingClientRect();(m.left<p.left||m.right>p.right)&&o.scrollIntoView({behavior:"smooth",block:"nearest",inline:"nearest"})}})}function Rt(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),me(t.id))}function Et(t){return["router-tab__item",{"is-active":i.activeId.value===t.id,"is-closable":be(t),"is-dragging":R.dragging&&R.dragTab?.id===t.id,"is-drag-over":R.dropIndex===u(t.id)},t.tabClass]}function Kt(t){const o=i.getRouteKey(t),s=i.tabs.find(m=>m.id===o);return s?s.alive:!1}function Bt(t){const o=i.getRouteKey(t);return!!i.tabs.find(m=>m.id===o)}function Pt(t,o,s){e.sortable&&(R.dragging=!0,R.dragIndex=o,R.dragTab=t,s.dataTransfer&&(s.dataTransfer.effectAllowed="move",s.dataTransfer.setData("text/plain",t.id)),a("tab-sort",{tab:t,index:o}))}function $t(t,o){!e.sortable||!R.dragging||(o.preventDefault(),o.dataTransfer&&(o.dataTransfer.dropEffect="move"))}function St(t){!e.sortable||!R.dragging||(R.dropIndex=t)}function At(){!e.sortable||R.dragging}function It(t,o){if(!(!e.sortable||!R.dragging)){if(o.preventDefault(),R.dragIndex!==-1&&R.dragIndex!==t){const s=i.tabs.splice(R.dragIndex,1)[0];i.tabs.splice(t,0,s),a("tab-sorted",{tab:s,fromIndex:R.dragIndex,toIndex:t})}Se()}}function Se(){R.dragging=!1,R.dragIndex=-1,R.dropIndex=-1,R.dragTab=null}n.onMounted(()=>{document.addEventListener("keydown",K)}),n.onBeforeUnmount(()=>{document.removeEventListener("keydown",K),r.appContext.config.globalProperties.$tabs=null,$.forEach(t=>{t.forEach(o=>{try{o()}catch(s){console.error("[RouterTab] Error during cleanup:",s)}})}),$.clear(),S.clear()}),n.watch(()=>e.keepAlive,t=>{i.options.keepAlive=t}),n.watch(()=>i.activeId.value,t=>{t&&me(t),K()}),n.watch(()=>i.tabs.length,()=>{const t=new Set(i.tabs.map(s=>s.id));Array.from(S.keys()).forEach(s=>{t.has(s)||(console.log(`[RouterTab] Cleaning up stale component instance: ${s}`),D(s))})}),n.watch(()=>e.contextmenu,t=>{t||K()}),n.watch(()=>F.value.length,t=>{C.visible&&t===0&&K()},{flush:"post"}),n.watch(F,t=>{if(!C.visible)return;L.value=new Array(t.length).fill(null);const o=ee(-1,1,t);Y(o)},{flush:"post"}),n.watch(()=>C.visible,t=>{t||(A.value=-1,L.value=[])});const pe=i.includeKeys;return{controller:i,tabs:i.tabs,includeKeys:pe,persistenceHydrating:M,componentCache:H,componentCacheTrigger:fe,cacheCurrentComponent:ht,tabTransitionProps:se,pageTransitionProps:ce,buildTabClass:Et,activate:Rt,close:wt,context:C,menuItems:F,handleMenuAction:Pe,showContextMenu:ut,hideContextMenu:K,getTabTitle:de,isClosable:be,isTabCached:Kt,isTabReady:Bt,hasCustomSlot:g,hasStartSlot:w,hasEndSlot:l,onDragStart:Pt,onDragOver:$t,onDragEnter:St,onDragLeave:At,onDrop:It,onDragEnd:Se,setupComponentWatching:x,cleanupComponentWatching:D,handleComponentRef:_,getReactiveTabTitle:gt,getComponentCacheKey:vt,createNamedComponent:$e,ensureNamedComponent:yt,getNamedComponent:Tt,shouldRenderRoute:kt,triggerTabUpdate:v,menuRef:Z,highlightedIndex:A,setMenuItemRef:bt,onMenuKeydown:pt,highlightMenuIndex:Y,scrollContainer:U,setTabRef:Ct,scrollTabIntoView:me}}}),ze=(e,a)=>{const r=e.__vccOpts||e;for(const[c,i]of a)r[c]=i;return r},Fe={class:"router-tab"},Ye={class:"router-tab__header"},He={class:"router-tab__scroll",ref:"scrollContainer"},We=["data-title","draggable","onClick","onAuxclick","onContextmenu","onDragstart","onDragover","onDragenter","onDrop"],Je=["title"],qe=["onClick"],Ge={class:"router-tab__container"},Xe={key:1,class:"router-tab__hydrating","aria-hidden":"true"},Qe=["aria-disabled","disabled","tabindex","onMouseenter","onClick"];function Ze(e,a,r,c,i,g){const w=n.resolveComponent("RouterView");return n.openBlock(),n.createElementBlock("div",Fe,[n.createElementVNode("header",Ye,[n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-start",{"has-content":e.hasStartSlot}])},[n.renderSlot(e.$slots,"start")],2),n.createElementVNode("div",He,[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,(l,y)=>(n.openBlock(),n.createElementBlock("li",{key:l.id,class:n.normalizeClass(e.buildTabClass(l)),"data-title":e.getTabTitle(l),draggable:e.sortable,ref_for:!0,ref:h=>e.setTabRef(l.id,h),onClick:h=>e.activate(l),onAuxclick:n.withModifiers(h=>e.close(l),["middle","prevent"]),onContextmenu:n.withModifiers(h=>e.showContextMenu(l,h),["prevent"]),onDragstart:h=>e.onDragStart(l,y,h),onDragover:h=>e.onDragOver(y,h),onDragenter:h=>e.onDragEnter(y),onDragleave:a[0]||(a[0]=(...h)=>e.onDragLeave&&e.onDragLeave(...h)),onDrop:h=>e.onDrop(y,h),onDragend:a[1]||(a[1]=(...h)=>e.onDragEnd&&e.onDragEnd(...h))},[l.icon?(n.openBlock(),n.createElementBlock("i",{key:0,class:n.normalizeClass(["router-tab__item-icon",l.icon])},null,2)):n.createCommentVNode("",!0),n.createElementVNode("span",{class:"router-tab__item-title",title:e.getReactiveTabTitle(l)},n.toDisplayString(e.getReactiveTabTitle(l)),9,Je),e.isClosable(l)?(n.openBlock(),n.createElementBlock("a",{key:1,class:"router-tab__item-close",onClick:n.withModifiers(h=>e.close(l),["stop"])},null,8,qe)):n.createCommentVNode("",!0)],42,We))),128))]),_:1},16)],512),n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-end",{"has-content":e.hasEndSlot}])},[n.renderSlot(e.$slots,"end")],2)]),n.createElementVNode("div",Ge,[e.persistenceHydrating?(n.openBlock(),n.createElementBlock("div",Xe)):(n.openBlock(),n.createBlock(w,{key:0},{default:n.withCtx(({Component:l,route:y})=>[e.hasCustomSlot?n.renderSlot(e.$slots,"default",n.normalizeProps(n.mergeProps({key:0},{Component:l,route:y,controller:e.controller,pageRef:h=>e.handleComponentRef(h,e.controller.getRouteKey(y))}))):(n.openBlock(),n.createElementBlock(n.Fragment,{key:1},[e.controller.options.keepAlive?(n.openBlock(),n.createBlock(n.KeepAlive,{key:0,include:e.includeKeys},[l?(n.openBlock(),n.createBlock(n.resolveDynamicComponent(e.getNamedComponent(l,e.getComponentCacheKey(y))),{key:e.getComponentCacheKey(y),ref:h=>e.handleComponentRef(h,e.controller.getRouteKey(y)),class:"router-tab-page"})):n.createCommentVNode("",!0)],1032,["include"])):(n.openBlock(),n.createBlock(n.Transition,n.mergeProps({key:1},e.pageTransitionProps,{appear:""}),{default:n.withCtx(()=>[l?(n.openBlock(),n.createBlock(n.resolveDynamicComponent(l),{key:e.controller.getRouteKey(y),ref:h=>e.handleComponentRef(h,e.controller.getRouteKey(y)),class:"router-tab-page"})):n.createCommentVNode("",!0)]),_:2},1040))],64))]),_:3}))]),n.withDirectives(n.createElementVNode("div",{ref:"menuRef",class:"router-tab__contextmenu",role:"menu",onKeydown:a[2]||(a[2]=(...l)=>e.onMenuKeydown&&e.onMenuKeydown(...l)),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,(l,y)=>(n.openBlock(),n.createElementBlock("a",{key:l.id,role:"menuitem",class:n.normalizeClass(["router-tab__contextmenu-item",{"is-focused":y===e.highlightedIndex}]),"aria-disabled":l.disabled,disabled:l.disabled,tabindex:l.disabled?-1:y===e.highlightedIndex?0:-1,ref_for:!0,ref:h=>e.setMenuItemRef(h,y),onMouseenter:h=>!l.disabled&&e.highlightMenuIndex(y),onClick:h=>e.handleMenuAction(l)},n.toDisplayString(l.label),43,Qe))),128))],36),[[n.vShow,e.context.visible&&e.context.target]])])}const ie=ze(Ue,[["render",Ze]]),et={class:"router-tabs","aria-hidden":"true"},G=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 ae(e),(r,c)=>(n.openBlock(),n.createElementBlock("span",et))}}),ve="tab-theme-style",we="tab-theme-primary-color",tt="system",Ce="(prefers-color-scheme: dark)";let j=null;const P={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"},nt={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 ot(e){if(typeof window>"u")return null;const a=window.localStorage.getItem(e);if(!a)return null;try{const r=JSON.parse(a);return r&&typeof r=="object"?r:null}catch{return null}}function le(e){typeof document>"u"||(document.documentElement.style.setProperty("--router-tab-primary",e.primary??P.primary),document.documentElement.style.setProperty("--router-tab-header-bg",e.headerBackground??P.headerBackground),document.documentElement.style.setProperty("--router-tab-background",e.background??P.background),document.documentElement.style.setProperty("--router-tab-active-background",e.activeBackground??P.activeBackground),document.documentElement.style.setProperty("--router-tab-text",e.text??P.text),document.documentElement.style.setProperty("--router-tab-active-text",e.activeText??P.activeText),document.documentElement.style.setProperty("--router-tab-border",e.border??P.border),document.documentElement.style.setProperty("--router-tab-active-border",e.activeBorder??P.activeBorder),document.documentElement.style.setProperty("--router-tab-button-color",e.buttonColor??P.buttonColor),document.documentElement.style.setProperty("--router-tab-active-button-color",e.activeButtonColor??P.activeButtonColor),document.documentElement.style.setProperty("--router-tab-button-background",e.buttonBackground??P.buttonBackground),document.documentElement.style.setProperty("--router-tab-active-button-background",e.activeButtonBackground??P.activeButtonBackground),document.documentElement.style.setProperty("--router-tab-icon-color",e.iconColor??P.iconColor))}function Re(e){if(typeof document>"u")return;const a=document.documentElement,r=window.matchMedia(Ce),c=()=>{a.dataset.theme=r.matches?"dark":"light"};j&&(r.removeEventListener("change",j),j=null),e==="system"?(c(),j=()=>c(),r.addEventListener("change",j)):a.dataset.theme=e}function Ee(e={}){if(typeof window>"u")return;const{styleKey:a=ve,primaryKey:r=we,defaultStyle:c=tt,defaultPrimary:i}=e,g=window.localStorage.getItem(a)??c;Re(g);const l=g==="dark"||g==="system"&&window.matchMedia(Ce).matches?{...nt}:{...P};i&&(l.primary=i);const y=ot(r);le(y?{...l,...y}:l)}function rt(e,a){if(typeof window>"u")return;const r=a?.styleKey??ve;window.localStorage.setItem(r,e),Re(e)}function at(e,a){if(typeof window>"u")return;const r=a?.primaryKey??we;window.localStorage.setItem(r,JSON.stringify(e)),le(e)}function X(e,a){if(n.isRef(e)){const c=!n.isReadonly(e);return{value:e,update:c?i=>{e.value=i}:()=>{}}}if(typeof e=="function"){const c=e;return{value:n.computed(c),update:()=>{}}}const r=n.ref(e===void 0?a:e);return{value:r,update:c=>{r.value=c}}}function Q(e={}){const a=X(e.title,"Untitled"),r=X(e.icon,""),c=X(e.closable,!0),i=X(e.meta,{});return{routeTabTitle:a.value,routeTabIcon:r.value,routeTabClosable:c.value,routeTabMeta:i.value,updateTitle:a.update,updateIcon:r.update,updateClosable:c.update,updateMeta:i.update}}function it(e,a="Page"){return Q({title:n.computed(()=>e.value?"Loading...":a),icon:n.computed(()=>e.value?"mdi-loading mdi-spin":"mdi-page"),closable:n.computed(()=>!e.value)})}function lt(e,a="Page",r="mdi-page"){return Q({title:n.computed(()=>e.value>0?`${a} (${e.value})`:a),icon:n.computed(()=>e.value>0?"mdi-bell-badge":r)})}function st(e,a="Page"){const r={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 Q({title:n.computed(()=>a+r[e.value].suffix),icon:n.computed(()=>r[e.value].icon),closable:n.computed(()=>e.value!=="loading")})}let Ke=!1;const ct={install(e,a){if(Ke)return;Ke=!0;const{initTheme:r=!0,themeOptions:c,componentName:i=ie.name||"RouterTab",tabsComponentName:g=G.name||"RouterTabs"}=a??{};r&&Ee(c??{}),e.component(i,ie),e.component(g,G),g.toLowerCase()!=="router-tabs"&&e.component("router-tabs",G),Object.defineProperty(e.config.globalProperties,"$tabs",{configurable:!0,enumerable:!1,get(){return e._context.provides[O]},set(w){w&&e.provide(O,w)}})}};E.RouterTab=ie,E.RouterTabs=G,E.default=ct,E.initRouterTabsTheme=Ee,E.routerTabsKey=O,E.setRouterTabsPrimary=at,E.setRouterTabsTheme=rt,E.useLoadingTab=it,E.useNotificationTab=lt,E.useReactiveTab=Q,E.useRouterTabs=re,E.useRouterTabsPersistence=ae,E.useStatusTab=st,Object.defineProperties(E,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
package/index.d.ts CHANGED
@@ -56,7 +56,7 @@ export declare const routerTabsKey: import('vue').InjectionKey<RouterTabsContext
56
56
 
57
57
  export declare function useRouterTabs(options?: { optional?: boolean }): RouterTabsContext | null
58
58
 
59
- export declare function useRouterTabsPersistence(options?: RouterTabsPersistenceOptions): void
59
+ export declare function useRouterTabsPersistence(options?: RouterTabsPersistenceOptions): { hydrating: import('vue').Ref<boolean> }
60
60
 
61
61
  export declare function initRouterTabsTheme(options?: RouterTabsThemeOptions): void
62
62
  export declare function setRouterTabsTheme(style: 'light' | 'dark' | 'system', options?: RouterTabsThemeOptions): void
@@ -53,7 +53,7 @@
53
53
  </header>
54
54
 
55
55
  <div class="router-tab__container">
56
- <RouterView v-slot="{ Component, route }">
56
+ <RouterView v-if="!persistenceHydrating" v-slot="{ Component, route }">
57
57
  <template v-if="hasCustomSlot">
58
58
  <slot
59
59
  v-bind="{
@@ -70,7 +70,7 @@
70
70
  <component
71
71
  v-if="Component"
72
72
  :is="getNamedComponent(Component, getComponentCacheKey(route))"
73
- :key="isRefreshing(route) ? getRefreshComponentKey(route) : getComponentCacheKey(route)"
73
+ :key="getComponentCacheKey(route)"
74
74
  :ref="(el: any) => handleComponentRef(el, controller.getRouteKey(route))"
75
75
  class="router-tab-page"
76
76
  />
@@ -89,6 +89,7 @@
89
89
  </template>
90
90
  </template>
91
91
  </RouterView>
92
+ <div v-else class="router-tab__hydrating" aria-hidden="true" />
92
93
  </div>
93
94
 
94
95
  <!-- Use v-show instead of v-if to keep DOM and prevent re-creation overhead -->
@@ -433,6 +434,7 @@ export default defineComponent({
433
434
  return false
434
435
  })
435
436
 
437
+ let persistenceHydrating = ref(false)
436
438
  if (props.cookieKey !== null || props.persistence) {
437
439
  const options: RouterTabsPersistenceOptions = {
438
440
  ...(props.persistence ?? {})
@@ -442,7 +444,8 @@ export default defineComponent({
442
444
  } else if (!options.cookieKey) {
443
445
  options.cookieKey = routerTabsCookie
444
446
  }
445
- useRouterTabsPersistence(options)
447
+ const persistenceState = useRouterTabsPersistence(options)
448
+ persistenceHydrating = persistenceState.hydrating
446
449
  }
447
450
 
448
451
  const tabTransitionProps = computed(() => getTransOpt(props.tabTransition))
@@ -885,14 +888,6 @@ export default defineComponent({
885
888
  return cacheKey
886
889
  }
887
890
 
888
- /**
889
- * Generates a special key for components in refreshing state.
890
- * Appends '::refresh' to ensure it's treated as separate from cached instance.
891
- */
892
- function getRefreshComponentKey(route: RouteLocationNormalizedLoaded): string {
893
- return `${getComponentCacheKey(route)}::refresh`
894
- }
895
-
896
891
  function isClosable(tab: TabRecord) {
897
892
  if (tab.closable === false) return false
898
893
  if (controller.options.keepLastTab && controller.tabs.length <= 1) return false
@@ -966,10 +961,6 @@ export default defineComponent({
966
961
  ]
967
962
  }
968
963
 
969
- function isRefreshing(route: RouteLocationNormalizedLoaded) {
970
- return controller.refreshingKey.value === controller.getRouteKey(route)
971
- }
972
-
973
964
  function isTabCached(route: RouteLocationNormalizedLoaded) {
974
965
  const routeKey = controller.getRouteKey(route)
975
966
  const tab = controller.tabs.find(tab => tab.id === routeKey)
@@ -1143,6 +1134,7 @@ export default defineComponent({
1143
1134
  controller,
1144
1135
  tabs: controller.tabs,
1145
1136
  includeKeys,
1137
+ persistenceHydrating,
1146
1138
  componentCache,
1147
1139
  componentCacheTrigger,
1148
1140
  cacheCurrentComponent,
@@ -1158,7 +1150,6 @@ export default defineComponent({
1158
1150
  hideContextMenu,
1159
1151
  getTabTitle,
1160
1152
  isClosable,
1161
- isRefreshing,
1162
1153
  isTabCached,
1163
1154
  isTabReady,
1164
1155
  hasCustomSlot,
@@ -1175,7 +1166,6 @@ export default defineComponent({
1175
1166
  handleComponentRef,
1176
1167
  getReactiveTabTitle,
1177
1168
  getComponentCacheKey,
1178
- getRefreshComponentKey,
1179
1169
  createNamedComponent,
1180
1170
  ensureNamedComponent,
1181
1171
  getNamedComponent,
@@ -507,7 +507,20 @@ export function createRouterTabs(
507
507
  const target = snapshot?.active ?? records[records.length - 1]?.to ?? options.defaultRoute
508
508
  if (target) {
509
509
  try {
510
- await router.replace(target)
510
+ const resolvedTarget = resolveRoute(router, target)
511
+ const currentRoute = router.currentRoute.value
512
+
513
+ // Avoid redundant navigation on page reload when the router is already at the target.
514
+ // A duplicate replace can trigger an extra mount cycle in tabbed layouts.
515
+ if (resolvedTarget.fullPath === currentRoute.fullPath) {
516
+ const tab = ensureTab(currentRoute as RouteLocationNormalizedLoaded)
517
+ activeId.value = tab.id
518
+ current.value = tab
519
+ enforceMaxAlive(tabs, options.maxAlive, activeId.value, aliveCache)
520
+ return
521
+ }
522
+
523
+ await router.replace(resolvedTarget)
511
524
  } catch (error) {
512
525
  console.warn('[RouterTabs] Failed to navigate to restored route', target, error)
513
526
  }
@@ -1,4 +1,4 @@
1
- import { nextTick, onMounted, ref, watch } from 'vue'
1
+ import { nextTick, onBeforeMount, onMounted, ref, watch, type Ref } from 'vue'
2
2
  import type { RouteLocationRaw } from 'vue-router'
3
3
  import { useRouterTabs } from './useRouterTabs'
4
4
  import type { RouterTabsSnapshot } from './core/types'
@@ -98,15 +98,18 @@ export function useRouterTabsPersistence(options: RouterTabsPersistenceOptions =
98
98
  } = options
99
99
 
100
100
  const controller = useRouterTabs({ optional: true })
101
+ // Start as `true` so pages won't mount until hydration decides what to do.
102
+ // This prevents double-mount / double-fetch on initial load when restoring tabs.
101
103
  const hydrating = ref(true)
102
104
 
103
- const setup = (ctrl: NonNullable<typeof controller>) => {
104
- onMounted(async () => {
105
- const initialSnapshot = deserialize(readCookie(cookieKey))
105
+ const setup = (ctrl: NonNullable<typeof controller>, mode: 'hook' | 'immediate' = 'hook') => {
106
+ const run = async () => {
107
+ hydrating.value = true
106
108
 
107
- if (initialSnapshot && initialSnapshot.tabs?.length) {
108
- try {
109
- hydrating.value = true
109
+ try {
110
+ const initialSnapshot = deserialize(readCookie(cookieKey))
111
+
112
+ if (initialSnapshot && initialSnapshot.tabs?.length) {
110
113
  await ctrl.hydrate(initialSnapshot)
111
114
  if (initialSnapshot.active) {
112
115
  await nextTick()
@@ -116,30 +119,30 @@ export function useRouterTabsPersistence(options: RouterTabsPersistenceOptions =
116
119
  ctrl.current.value = activeTab
117
120
  }
118
121
  }
119
- } finally {
120
- hydrating.value = false
121
- }
122
- } else if (Object.prototype.hasOwnProperty.call(options, 'fallbackRoute')) {
123
- try {
124
- hydrating.value = true
122
+ } else if (Object.prototype.hasOwnProperty.call(options, 'fallbackRoute')) {
125
123
  const fallback = options.fallbackRoute ?? ctrl.options.defaultRoute
126
124
  await ctrl.reset(fallback)
127
- } finally {
128
- hydrating.value = false
129
125
  }
130
- } else {
131
- hydrating.value = false
132
- }
133
126
 
134
- const snapshot = ctrl.snapshot()
135
- if (!snapshot.tabs.length) {
136
- removeCookie(cookieKey, options)
137
- } else {
138
- writeCookie(cookieKey, serialize(snapshot), options)
127
+ const snapshot = ctrl.snapshot()
128
+ if (!snapshot.tabs.length) {
129
+ removeCookie(cookieKey, options)
130
+ } else {
131
+ writeCookie(cookieKey, serialize(snapshot), options)
132
+ }
133
+ } finally {
134
+ hydrating.value = false
139
135
  }
136
+ }
140
137
 
141
- hydrating.value = false
142
- })
138
+ if (mode === 'immediate') {
139
+ void run()
140
+ } else {
141
+ // Run before the first paint to avoid mounting pages twice during a restore.
142
+ onBeforeMount(() => {
143
+ void run()
144
+ })
145
+ }
143
146
 
144
147
  watch(
145
148
  () => ({
@@ -173,12 +176,14 @@ export function useRouterTabsPersistence(options: RouterTabsPersistenceOptions =
173
176
  onMounted(() => {
174
177
  const lateController = useRouterTabs({ optional: true })
175
178
  if (lateController) {
176
- setup(lateController)
179
+ setup(lateController, 'immediate')
177
180
  } else if (import.meta.env?.DEV) {
178
181
  console.warn('[RouterTabs] Persistence helper must be used inside <router-tab>.')
179
182
  }
180
183
  })
181
184
  }
185
+
186
+ return { hydrating: hydrating as Ref<boolean> }
182
187
  }
183
188
 
184
189
  export default useRouterTabsPersistence
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue3-router-tab",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",