vue3-router-tab 1.3.6 → 1.3.7

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(R,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):(R=typeof globalThis<"u"?globalThis:R||self,n(R["vue3-router-tab"]={},R.Vue,R.VueRouter))})(this,(function(R,n,Ae){"use strict";function Ke(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 o=e.resolve(a);if(!o||!o.matched.length)throw new Error(`[RouterTabs] Unable to resolve route: ${String(a)}`);return o}const Ie={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 o=a(e);if(typeof o=="string"&&o.length)return o}else if(typeof a=="string"&&a.length){const o=Ie[a.toLowerCase()];return o?o(e):a}return e.fullPath}function Q(e,a){const o=e.meta?.keepAlive;return typeof o=="boolean"?o:a}function Z(e,a){const o=e.meta?.reuse;return typeof o=="boolean"?o:a}function be(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 Y(e,a,o){const l=be(e);return{id:M(e),to:e.fullPath,fullPath:e.fullPath,matched:e,alive:Q(e,o),reusable:Z(e,!1),closable:l.closable??!0,renderKey:typeof a.renderKey=="number"?a.renderKey:0,...l,...a}}function ee(e,a,o,l){if(!e.find(g=>g.id===a.id)){if(o==="next"&&l){const g=e.findIndex(v=>v.id===l);if(g!==-1){e.splice(g+1,0,a);return}}e.push(a)}}function te(e,a,o){if(!a||a<=0)return;const l=e.filter(i=>i.alive);for(;l.length>a;){const i=l.shift();if(!i||i.id===o)continue;const g=e.findIndex(v=>v.id===i.id);g>-1&&(e[g].alive=!1)}}function $e(e){return{to:e.to,title:e.title,tips:e.tips,icon:e.icon,tabClass:e.tabClass,closable:e.closable,renderKey:e.renderKey}}function Se(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 De(e,a={}){const o=Ke(a),l=n.reactive([]),i=n.ref(null),g=n.shallowRef(),v=n.ref(null),c=n.computed(()=>l.filter(s=>s.alive).map(s=>`${s.id}::${s.renderKey??0}`));let d=!1;function y(s){const b=typeof s.matched=="object"?s:x(e,s);return{key:M(b),fullPath:b.fullPath,alive:Q(b,o.keepAlive),reusable:Z(b,!1),matched:b}}function P(s){const b=M(s);let m=l.find(T=>T.id===b);return m?(m.fullPath=s.fullPath,m.to=s.fullPath,m.matched=s,m.alive=Q(s,o.keepAlive),m.reusable=Z(s,m.reusable),typeof m.renderKey!="number"&&(m.renderKey=0),Object.assign(m,be(s)),m):(m=Y(s,{},o.keepAlive),ee(l,m,o.appendPosition,i.value),te(l,o.maxAlive,i.value),m)}async function A(s,b=!1,m=!0){const T=x(e,s),C=M(T),I=i.value===C;m==="sameTab"&&(m=I),m&&await L(C,!0),await e[b?"replace":"push"](T),I&&await U()}function S(s){const b=l.findIndex(E=>E.id===s);if(b===-1)return o.defaultRoute;const m=l[b+1],T=l[b-1],C=l.find(E=>E.id!==s),I=m||T||C;return I?I.to:o.defaultRoute}async function N(s=i.value,b={}){if(!s)return;if(!b.force&&o.keepLastTab&&l.length===1)throw new Error("[RouterTabs] Unable to close the final tab when keepLastTab is true.");const T=i.value===s&&b.redirect!==null,C=T?b.redirect??S(s):null;await K(s,{force:b.force}),b.redirect!==null&&T&&C&&await e.replace(C)}async function K(s,b={}){const m=l.findIndex(T=>T.id===s);m!==-1&&(l.splice(m,1),v.value===s&&(v.value=null),i.value===s&&(i.value=null,g.value=void 0))}async function L(s=i.value??void 0,b=!1){if(!s)return;const m=l.find(C=>C.id===s);if(!m)return;const T=o.keepAlive&&m.alive;T&&(m.alive=!1,await n.nextTick()),m.renderKey=(m.renderKey??0)+1,T&&(m.alive=!0),v.value=s,await n.nextTick(),b||await n.nextTick(),v.value=null}async function ie(s=!1){for(const b of l)await L(b.id,s)}function le(s,b){const m=l.find(T=>T.id===s);m&&(m.alive=!!b,m.alive&&te(l,o.maxAlive,i.value))}function k(s){const b=l.find(m=>m.id===s);b&&(b.alive&&(b.alive=!1),b.renderKey=(b.renderKey??0)+1)}function G(){l.forEach(s=>{s.alive=!1})}function D(){return c.value.slice()}async function $(s=o.defaultRoute){l.splice(0,l.length),i.value=null,g.value=void 0;for(const b of o.initialTabs){const m=x(e,b.to),T=Y(m,b,o.keepAlive);l.push(T)}await e.replace(s)}async function U(){const s=i.value;s&&await L(s,!0)}function z(s){return typeof s.matched=="object"?M(s):M(x(e,s))}function w(){const s=l.find(b=>b.id===i.value);return{tabs:l.map($e),active:s?s.to:null}}async function se(s){d=!0,l.splice(0,l.length),i.value=null,g.value=void 0;const b=s?.tabs??[];for(const T of b)try{const C=x(e,T.to),I=Se(T),E=Y(C,I,o.keepAlive);ee(l,E,"last",null)}catch(C){console.warn("[RouterTabs] Failed to restore tab",T,C)}d=!1;const m=s?.active??b[b.length-1]?.to??o.defaultRoute;if(m)try{await e.replace(m)}catch(T){console.warn("[RouterTabs] Failed to navigate to restored route",m,T)}}return n.watch(()=>e.currentRoute.value,s=>{if(d)return;const b=P(s);i.value=b.id,g.value=b,te(l,o.maxAlive,i.value)},{immediate:!0}),o.initialTabs.length&&o.initialTabs.forEach(s=>{const b=x(e,s.to),m=Y(b,s,o.keepAlive);ee(l,m,"last",null)}),{options:o,tabs:l,activeId:i,current:g,includeKeys:c,refreshingKey:v,openTab:A,closeTab:N,removeTab:K,refreshTab:L,refreshAll:ie,setTabAlive:le,evictCache:k,clearCache:G,getCacheKeys:D,reset:$,reload:U,getRouteKey:z,matchRoute:y,snapshot:w,hydrate:se,ensureTab:P}}function me(e){return e?typeof e=="string"?{name:e}:e:{}}const O=Symbol("RouterTabsContext"),H="router-tabs:snapshot";function ne(e={}){const{optional:a=!1}=e,o=n.inject(O,null);if(o)return o;const l=n.inject("$tabs",null);if(l)return l;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 xe=864e5;function Me(e){if(typeof document>"u")return null;const a=`${encodeURIComponent(e)}=`,o=document.cookie?document.cookie.split("; "):[];for(const l of o)if(l.startsWith(a))return decodeURIComponent(l.slice(a.length));return null}function pe(e,a,o){if(typeof document>"u")return;const{expiresInDays:l=7,path:i="/",domain:g,secure:v,sameSite:c="lax"}=o,d=[`${encodeURIComponent(e)}=${encodeURIComponent(a)}`];if(l!==1/0){const y=new Date(Date.now()+l*xe).toUTCString();d.push(`Expires=${y}`)}i&&d.push(`Path=${i}`),g&&d.push(`Domain=${g}`),v&&d.push("Secure"),c&&d.push(`SameSite=${c.charAt(0).toUpperCase()}${c.slice(1)}`),document.cookie=d.join("; ")}function ge(e,a){if(typeof document>"u")return;const{path:o="/",domain:l}=a,i=[`${encodeURIComponent(e)}=`];i.push("Expires=Thu, 01 Jan 1970 00:00:01 GMT"),o&&i.push(`Path=${o}`),l&&i.push(`Domain=${l}`),document.cookie=i.join("; ")}const Le=e=>JSON.stringify(e??null),Ve=e=>{if(!e)return null;try{return JSON.parse(e)}catch{return null}};function oe(e={}){const{cookieKey:a=H,serialize:o=Le,deserialize:l=Ve}=e,i=ne({optional:!0}),g=n.ref(!0),v=c=>{n.onMounted(async()=>{const d=l(Me(a));if(d&&d.tabs?.length)try{if(g.value=!0,await c.hydrate(d),d.active){await n.nextTick();const P=c.tabs.find(A=>A.to===d.active);P&&(c.activeId.value=P.id,c.current.value=P)}}finally{g.value=!1}else if(Object.prototype.hasOwnProperty.call(e,"fallbackRoute"))try{g.value=!0;const P=e.fallbackRoute??c.options.defaultRoute;await c.reset(P)}finally{g.value=!1}else g.value=!1;const y=c.snapshot();y.tabs.length?pe(a,o(y),e):ge(a,e),g.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(g.value)return;const d=c.snapshot();d.tabs.length?pe(a,o(d),e):ge(a,e)},{deep:!0})};i?v(i):n.onMounted(()=>{const c=ne({optional:!0});c&&v(c)})}const Oe=n.defineComponent({name:"RouterTab",components:{RouterView:Ae.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:H},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 l=o.appContext.app.config.globalProperties.$router;if(!l)throw new Error("[RouterTab] Vue Router is required. Make sure to call app.use(router) before RouterTab.");const i=De(l,{initialTabs:e.tabs,keepAlive:e.keepAlive,maxAlive:e.maxAlive,keepLastTab:e.keepLastTab,appendPosition:e.append,defaultRoute:e.defaultPage});n.provide(O,i),o.appContext.config.globalProperties.$tabs=i;const g=n.computed(()=>!!o?.slots?.default),v=n.computed(()=>!!o?.slots?.start),c=n.computed(()=>!!o?.slots?.end),d=n.ref(0),y=n.computed(()=>{d.value;const t={};return i.tabs.forEach(r=>{const u=typeof r.title=="string"?r.title:String(r.title||ce(r));t[r.id]=u}),t});function P(){d.value++}const A=new Map,S=new Map;function N(t,r){if(!(!r||A.has(t)))try{A.set(t,r);const u=i.tabs.find(p=>i.getRouteKey(p.to)===t);if(!u){console.warn(`[RouterTab] Cannot setup watching: tab not found for ${t}`);return}const h=[];if(r.routeTabTitle!==void 0)try{const p=n.watch(()=>{const f=r.routeTabTitle;return f&&typeof f=="object"&&"value"in f?f.value:f},f=>{if(f!=null){const V=String(f);u.title=V,P()}},{immediate:!0});h.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabTitle for ${t}:`,p)}if(r.routeTabIcon!==void 0)try{const p=n.watch(()=>{const f=r.routeTabIcon;return f&&typeof f=="object"&&"value"in f?f.value:f},f=>{f!=null&&(u.icon=String(f),P())},{immediate:!0});h.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabIcon for ${t}:`,p)}if(r.routeTabClosable!==void 0)try{const p=n.watch(()=>{const f=r.routeTabClosable;return f&&typeof f=="object"&&"value"in f?f.value:f},f=>{f!=null&&(u.closable=!!f,P())},{immediate:!0});h.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabClosable for ${t}:`,p)}if(r.routeTabMeta!==void 0)try{const p=n.watch(()=>{const f=r.routeTabMeta;return f&&typeof f=="object"&&"value"in f?f.value:f},f=>{f&&typeof f=="object"&&(Object.assign(u,f),P())},{immediate:!0,deep:!0});h.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabMeta for ${t}:`,p)}S.set(t,h)}catch(u){console.error(`[RouterTab] Error in setupComponentWatching for ${t}:`,u),K(t)}}function K(t){try{const r=S.get(t);r&&(r.forEach(u=>{try{u()}catch(h){console.error(`[RouterTab] Error cleaning up watcher for ${t}:`,h)}}),S.delete(t)),A.delete(t)}catch(r){console.error(`[RouterTab] Error in cleanupComponentWatching for ${t}:`,r)}}function L(t,r){try{t?t.routeTabTitle!==void 0||t.routeTabIcon!==void 0||t.routeTabClosable!==void 0?N(r,t):t.$&&(t.$.routeTabTitle!==void 0||t.$.routeTabIcon!==void 0||t.$.routeTabClosable!==void 0)&&N(r,t.$):t===null&&K(r)}catch(u){console.error(`[RouterTab] Error handling component ref for ${r}:`,u),K(r)}}if(n.onErrorCaptured((t,r,u)=>{if(console.error("[RouterTab] Error captured from component:",t,u),r&&i.activeId.value){const h=i.activeId.value;K(h);const p=i.tabs.find(f=>f.id===h);p&&p.alive&&(console.warn(`[RouterTab] Removing errored component ${h} from KeepAlive cache`),p.alive=!1,n.nextTick(()=>{p.alive=!0}))}return!1}),e.cookieKey!==null||e.persistence){const t={...e.persistence??{}};e.cookieKey!==null?t.cookieKey=e.cookieKey||H:t.cookieKey||(t.cookieKey=H),oe(t)}const ie=n.computed(()=>me(e.tabTransition)),le=n.computed(()=>me(e.pageTransition)),k=n.reactive({visible:!1,target:null,position:{x:0,y:0}}),G=n.ref(null),D=n.ref([]),$=n.ref(-1),U=n.ref(null),z=new Map,w=n.reactive({dragging:!1,dragIndex:-1,dropIndex:-1,dragTab:null}),se=["refresh","refreshAll","close","closeLefts","closeRights","closeOthers"];function s(t){return i.tabs.findIndex(r=>r.id===t)}function b(t){const r=s(t.id);return r>0?i.tabs.slice(0,r):[]}function m(t){const r=s(t.id);return r>-1?i.tabs.slice(r+1):[]}function T(t){return i.tabs.filter(r=>r.id!==t.id)}async function C(t,r){const u=t.filter(h=>h.closable!==!1);if(u.length){for(const h of u)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 I={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})=>ue(t)},closeLefts:{label:"Close to the Left",handler:async({target:t})=>{await C(b(t),t)},enable:({target:t})=>b(t).some(r=>r.closable!==!1)},closeRights:{label:"Close to the Right",handler:async({target:t})=>{await C(m(t),t)},enable:({target:t})=>m(t).some(r=>r.closable!==!1)},closeOthers:{label:"Close Others",handler:async({target:t})=>{await C(T(t),t)},enable:({target:t})=>T(t).some(r=>r.closable!==!1)}};function E(){k.visible=!1,k.target=null,$.value=-1,D.value=[]}function at(t,r){e.contextmenu&&(k.target=t,k.position.x=r.clientX,k.position.y=r.clientY,n.nextTick(()=>{k.visible=!0,document.addEventListener("click",E,{once:!0}),n.nextTick(()=>{lt()})}))}function it(t,r){const u=typeof t=="string"?{id:t}:t,h=I[u.id],p=u.label??h?.label??String(u.id),f=u.visible??h?.visible??!0;if(!(typeof f=="function"?f(r):f!==!1))return null;const de=u.enable??h?.enable??!0,Et=typeof de=="function"?de(r):de!==!1,Pe=u.handler??h?.handler;if(!Pe)return null;const Bt=async()=>{await Promise.resolve(Pe(r))};return{id:String(u.id),label:p,disabled:!Et,action:Bt}}const _=n.computed(()=>{if(!k.visible||!k.target||e.contextmenu===!1)return[];const t=Array.isArray(e.contextmenu)?e.contextmenu:se,r={target:k.target,controller:i};return t.map(u=>it(u,r)).filter(u=>!!u)});function lt(){const t=G.value;if(!t)return;const r=8,{innerWidth:u,innerHeight:h}=window,p=t.getBoundingClientRect();let f=k.position.x,V=k.position.y;p.right>u-r&&(f=Math.max(r,u-p.width-r)),p.bottom>h-r&&(V=Math.max(r,h-p.height-r)),(f!==k.position.x||V!==k.position.y)&&(k.position.x=f,k.position.y=V)}function st(t,r){D.value[r]=t??null}function ct(t){if(t<0)return;D.value[t]?.focus({preventScroll:!0})}function X(t,r,u=_.value){if(!u.length)return-1;const h=u.length;let p=t;for(let f=0;f<h;f++)if(p=(p+r+h)%h,!u[p].disabled)return p;return-1}function F(t){$.value=t,!(t<0)&&n.nextTick(()=>ct(t))}function Re(t){const r=X($.value,t);r!==-1&&F(r)}function ut(t){if(!k.visible)return;const r=t.key,u=_.value;if(!u.length)return;if(r==="Tab"){E();return}if(["ArrowDown","ArrowUp","ArrowRight","ArrowLeft","Home","End","Enter"," ","Spacebar","Escape"].includes(r))switch(t.preventDefault(),r){case"ArrowDown":case"ArrowRight":Re(1);break;case"ArrowUp":case"ArrowLeft":Re(-1);break;case"Home":F(X(-1,1));break;case"End":F(X(u.length,-1));break;case"Enter":case" ":case"Spacebar":{const p=$.value;if(p>-1){const f=u[p];f.disabled||Ce(f)}break}case"Escape":E();break}}async function Ce(t){t.disabled||(E(),await t.action())}function ce(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 ft(t){return y.value[t.id]||ce(t)}function Ee(t){const r=i.getRouteKey(t),u=i.tabs.find(p=>p.id===r);if(!u)return console.warn("[RouterTab] Tab not found for route:",r),`${r}::0`;const h=u.renderKey??0;return`${r}::${h}`}function dt(t){return`${Ee(t)}::refresh`}function ue(t){return!(t.closable===!1||i.options.keepLastTab&&i.tabs.length<=1)}async function bt(t){await i.closeTab(t.id)}function mt(t,r){r?z.set(t,r):z.delete(t)}function fe(t){n.nextTick(()=>{const r=z.get(t),u=U.value;if(r&&u){const h=r.getBoundingClientRect(),p=u.getBoundingClientRect();(h.left<p.left||h.right>p.right)&&r.scrollIntoView({behavior:"smooth",block:"nearest",inline:"nearest"})}})}function pt(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),fe(t.id))}function gt(t){return["router-tab__item",{"is-active":i.activeId.value===t.id,"is-closable":ue(t),"is-dragging":w.dragging&&w.dragTab?.id===t.id,"is-drag-over":w.dropIndex===s(t.id)},t.tabClass]}function ht(t){return i.refreshingKey.value===i.getRouteKey(t)}function yt(t){const r=i.getRouteKey(t),u=i.tabs.find(h=>h.id===r);return u?i.options.keepAlive?u.alive:!0:!1}function Tt(t,r,u){e.sortable&&(w.dragging=!0,w.dragIndex=r,w.dragTab=t,u.dataTransfer&&(u.dataTransfer.effectAllowed="move",u.dataTransfer.setData("text/plain",t.id)),a("tab-sort",{tab:t,index:r}))}function vt(t,r){!e.sortable||!w.dragging||(r.preventDefault(),r.dataTransfer&&(r.dataTransfer.dropEffect="move"))}function kt(t){!e.sortable||!w.dragging||(w.dropIndex=t)}function wt(){!e.sortable||w.dragging}function Rt(t,r){if(!(!e.sortable||!w.dragging)){if(r.preventDefault(),w.dragIndex!==-1&&w.dragIndex!==t){const u=i.tabs.splice(w.dragIndex,1)[0];i.tabs.splice(t,0,u),a("tab-sorted",{tab:u,fromIndex:w.dragIndex,toIndex:t})}Be()}}function Be(){w.dragging=!1,w.dragIndex=-1,w.dropIndex=-1,w.dragTab=null}n.onMounted(()=>{document.addEventListener("keydown",E)}),n.onBeforeUnmount(()=>{document.removeEventListener("keydown",E),o.appContext.config.globalProperties.$tabs=null,S.forEach(t=>{t.forEach(r=>{try{r()}catch(u){console.error("[RouterTab] Error during cleanup:",u)}})}),S.clear(),A.clear()}),n.watch(()=>e.keepAlive,t=>{i.options.keepAlive=t}),n.watch(()=>i.activeId.value,t=>{t&&fe(t),E()}),n.watch(()=>i.tabs.length,()=>{const t=new Set(i.tabs.map(u=>u.id));Array.from(A.keys()).forEach(u=>{t.has(u)||(console.log(`[RouterTab] Cleaning up stale component instance: ${u}`),K(u))})}),n.watch(()=>e.contextmenu,t=>{t||E()}),n.watch(()=>_.value.length,t=>{k.visible&&t===0&&E()},{flush:"post"}),n.watch(_,t=>{if(!k.visible)return;D.value=new Array(t.length).fill(null);const r=X(-1,1,t);F(r)},{flush:"post"}),n.watch(()=>k.visible,t=>{t||($.value=-1,D.value=[])});const Ct=i.includeKeys;return{controller:i,tabs:i.tabs,includeKeys:Ct,tabTransitionProps:ie,pageTransitionProps:le,buildTabClass:gt,activate:pt,close:bt,context:k,menuItems:_,handleMenuAction:Ce,showContextMenu:at,hideContextMenu:E,getTabTitle:ce,isClosable:ue,isRefreshing:ht,isTabReady:yt,hasCustomSlot:g,hasStartSlot:v,hasEndSlot:c,onDragStart:Tt,onDragOver:vt,onDragEnter:kt,onDragLeave:wt,onDrop:Rt,onDragEnd:Be,setupComponentWatching:N,cleanupComponentWatching:K,handleComponentRef:L,getReactiveTabTitle:ft,getComponentCacheKey:Ee,getRefreshComponentKey:dt,triggerTabUpdate:P,menuRef:G,highlightedIndex:$,setMenuItemRef:st,onMenuKeydown:ut,highlightMenuIndex:F,scrollContainer:U,setTabRef:mt,scrollTabIntoView:fe}}}),je=(e,a)=>{const o=e.__vccOpts||e;for(const[l,i]of a)o[l]=i;return o},Ne={class:"router-tab"},Ue={class:"router-tab__header"},ze={class:"router-tab__scroll",ref:"scrollContainer"},_e=["data-title","draggable","onClick","onAuxclick","onContextmenu","onDragstart","onDragover","onDragenter","onDrop"],Fe=["title"],Ye=["onClick"],He={class:"router-tab__container"},We=["aria-disabled","disabled","tabindex","onMouseenter","onClick"];function Je(e,a,o,l,i,g){const v=n.resolveComponent("RouterView");return n.openBlock(),n.createElementBlock("div",Ne,[n.createElementVNode("header",Ue,[n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-start",{"has-content":e.hasStartSlot}])},[n.renderSlot(e.$slots,"start")],2),n.createElementVNode("div",ze,[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,ref_for:!0,ref:y=>e.setTabRef(c.id,y),onClick:y=>e.activate(c),onAuxclick:n.withModifiers(y=>e.close(c),["middle","prevent"]),onContextmenu:n.withModifiers(y=>e.showContextMenu(c,y),["prevent"]),onDragstart:y=>e.onDragStart(c,d,y),onDragover:y=>e.onDragOver(d,y),onDragenter:y=>e.onDragEnter(d),onDragleave:a[0]||(a[0]=(...y)=>e.onDragLeave&&e.onDragLeave(...y)),onDrop:y=>e.onDrop(d,y),onDragend:a[1]||(a[1]=(...y)=>e.onDragEnd&&e.onDragEnd(...y))},[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,Fe),e.isClosable(c)?(n.openBlock(),n.createElementBlock("a",{key:1,class:"router-tab__item-close",onClick:n.withModifiers(y=>e.close(c),["stop"])},null,8,Ye)):n.createCommentVNode("",!0)],42,_e))),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",He,[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.createBlock(n.Transition,n.mergeProps({key:1},e.pageTransitionProps,{appear:""}),{default:n.withCtx(()=>[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"})):e.controller.options.keepAlive?(n.openBlock(),n.createBlock(n.KeepAlive,{key:1,include:e.includeKeys,max:e.controller.options.maxAlive||void 0},[e.isTabReady(c.route)?(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"})):n.createCommentVNode("",!0)],1032,["include","max"])):e.isTabReady(c.route)?(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"})):n.createCommentVNode("",!0)]),_:2},1040))]),_:3})]),n.withDirectives(n.createElementVNode("div",{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:y=>e.setMenuItemRef(y,d),onMouseenter:y=>!c.disabled&&e.highlightMenuIndex(d),onClick:y=>e.handleMenuAction(c)},n.toDisplayString(c.label),43,We))),128))],36),[[n.vShow,e.context.visible&&e.context.target]])])}const re=je(Oe,[["render",Je]]),qe={class:"router-tabs","aria-hidden":"true"},W=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 oe(e),(o,l)=>(n.openBlock(),n.createElementBlock("span",qe))}}),he="tab-theme-style",ye="tab-theme-primary-color",Ge="system",Te="(prefers-color-scheme: dark)";let j=null;const B={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"},Xe={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 Qe(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 ae(e){typeof document>"u"||(document.documentElement.style.setProperty("--router-tab-primary",e.primary??B.primary),document.documentElement.style.setProperty("--router-tab-header-bg",e.headerBackground??B.headerBackground),document.documentElement.style.setProperty("--router-tab-background",e.background??B.background),document.documentElement.style.setProperty("--router-tab-active-background",e.activeBackground??B.activeBackground),document.documentElement.style.setProperty("--router-tab-text",e.text??B.text),document.documentElement.style.setProperty("--router-tab-active-text",e.activeText??B.activeText),document.documentElement.style.setProperty("--router-tab-border",e.border??B.border),document.documentElement.style.setProperty("--router-tab-active-border",e.activeBorder??B.activeBorder),document.documentElement.style.setProperty("--router-tab-button-color",e.buttonColor??B.buttonColor),document.documentElement.style.setProperty("--router-tab-active-button-color",e.activeButtonColor??B.activeButtonColor),document.documentElement.style.setProperty("--router-tab-button-background",e.buttonBackground??B.buttonBackground),document.documentElement.style.setProperty("--router-tab-active-button-background",e.activeButtonBackground??B.activeButtonBackground),document.documentElement.style.setProperty("--router-tab-icon-color",e.iconColor??B.iconColor))}function ve(e){if(typeof document>"u")return;const a=document.documentElement,o=window.matchMedia(Te),l=()=>{a.dataset.theme=o.matches?"dark":"light"};j&&(o.removeEventListener("change",j),j=null),e==="system"?(l(),j=()=>l(),o.addEventListener("change",j)):a.dataset.theme=e}function ke(e={}){if(typeof window>"u")return;const{styleKey:a=he,primaryKey:o=ye,defaultStyle:l=Ge,defaultPrimary:i}=e,g=window.localStorage.getItem(a)??l;ve(g);const c=g==="dark"||g==="system"&&window.matchMedia(Te).matches?{...Xe}:{...B};i&&(c.primary=i);const d=Qe(o);ae(d?{...c,...d}:c)}function Ze(e,a){if(typeof window>"u")return;const o=a?.styleKey??he;window.localStorage.setItem(o,e),ve(e)}function et(e,a){if(typeof window>"u")return;const o=a?.primaryKey??ye;window.localStorage.setItem(o,JSON.stringify(e)),ae(e)}function J(e,a){if(n.isRef(e)){const l=!n.isReadonly(e);return{value:e,update:l?i=>{e.value=i}:()=>{}}}if(typeof e=="function"){const l=e;return{value:n.computed(l),update:()=>{}}}const o=n.ref(e===void 0?a:e);return{value:o,update:l=>{o.value=l}}}function q(e={}){const a=J(e.title,"Untitled"),o=J(e.icon,""),l=J(e.closable,!0),i=J(e.meta,{});return{routeTabTitle:a.value,routeTabIcon:o.value,routeTabClosable:l.value,routeTabMeta:i.value,updateTitle:a.update,updateIcon:o.update,updateClosable:l.update,updateMeta:i.update}}function tt(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 nt(e,a="Page",o="mdi-page"){return q({title:n.computed(()=>e.value>0?`${a} (${e.value})`:a),icon:n.computed(()=>e.value>0?"mdi-bell-badge":o)})}function ot(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 q({title:n.computed(()=>a+o[e.value].suffix),icon:n.computed(()=>o[e.value].icon),closable:n.computed(()=>e.value!=="loading")})}let we=!1;const rt={install(e,a){if(we)return;we=!0;const{initTheme:o=!0,themeOptions:l,componentName:i=re.name||"RouterTab",tabsComponentName:g=W.name||"RouterTabs"}=a??{};o&&ke(l??{}),e.component(i,re),e.component(g,W),g.toLowerCase()!=="router-tabs"&&e.component("router-tabs",W),Object.defineProperty(e.config.globalProperties,"$tabs",{configurable:!0,enumerable:!1,get(){return e._context.provides[O]},set(v){v&&e.provide(O,v)}})}};R.RouterTab=re,R.RouterTabs=W,R.default=rt,R.initRouterTabsTheme=ke,R.routerTabsKey=O,R.setRouterTabsPrimary=et,R.setRouterTabsTheme=Ze,R.useLoadingTab=tt,R.useNotificationTab=nt,R.useReactiveTab=q,R.useRouterTabs=ne,R.useRouterTabsPersistence=oe,R.useStatusTab=ot,Object.defineProperties(R,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
1
+ (function(C,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):(C=typeof globalThis<"u"?globalThis:C||self,n(C["vue3-router-tab"]={},C.Vue,C.VueRouter))})(this,(function(C,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 D(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 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=De[a.toLowerCase()];return r?r(e):a}return e.fullPath}function ee(e,a){const r=e.meta?.keepAlive;return typeof r=="boolean"?r:a}function te(e,a){const r=e.meta?.reuse;return typeof r=="boolean"?r:a}function ge(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 q(e,a,r){const c=ge(e);return{id:M(e),to:e.fullPath,fullPath:e.fullPath,matched:e,alive:ee(e,r),reusable:te(e,!1),closable:c.closable??!0,renderKey:typeof a.renderKey=="number"?a.renderKey:0,...c,...a}}function ne(e,a,r,c){if(!e.find(g=>g.id===a.id)){if(r==="next"&&c){const g=e.findIndex(k=>k.id===c);if(g!==-1){e.splice(g+1,0,a);return}}e.push(a)}}function oe(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 k=e.findIndex(l=>l.id===g.id);if(k>-1){const l=e[k],m=`${l.id}::${l.renderKey??0}`;c.delete(m),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(),k=n.ref(null),l=n.reactive(new Set),m=n.computed(()=>Array.from(l));let y=!1;function $(u){const d=typeof u.matched=="object"?u:D(e,u);return{key:M(d),fullPath:d.fullPath,alive:ee(d,r.keepAlive),reusable:te(d,!1),matched:d}}function P(u){const d=M(u);let f=c.find(v=>v.id===d);const T=ee(u,r.keepAlive);if(f){f.fullPath=u.fullPath,f.to=u.fullPath,f.matched=u,f.reusable=te(u,f.reusable),typeof f.renderKey!="number"&&(f.renderKey=0);const v=`${d}::${f.renderKey}`;return(d.includes("students")||d.includes("classroom")||d.includes("quiz"))&&console.log(`[ensureTab] EXISTING tab: ${u.fullPath}`,{key:d,shouldBeAlive:T,currentRenderKey:f.renderKey,currentCacheKey:v,isInCache:l.has(v),aliveCacheSize:l.size,cacheContents:Array.from(l)}),T&&(l.has(v)?f.alive||(f.alive=!0,(d.includes("students")||d.includes("classroom")||d.includes("quiz"))&&console.log(`[ensureTab] ✅ Reactivated: ${v}`)):(l.add(v),f.alive=!0,(d.includes("students")||d.includes("classroom")||d.includes("quiz"))&&console.log(`[ensureTab] ✅ Added to cache: ${v}`))),Object.assign(f,ge(u)),f}if(f=q(u,{},r.keepAlive),f.alive){const v=`${d}::${f.renderKey??0}`;l.add(v),(d.includes("students")||d.includes("classroom")||d.includes("quiz"))&&console.log(`[ensureTab] NEW tab created and cached: ${v}`)}return ne(c,f,r.appendPosition,i.value),oe(c,r.maxAlive,i.value,l),f}async function A(u,d=!1,f="sameTab"){const T=D(e,u),v=M(T),E=i.value===v;f==="sameTab"&&(f=E),f&&await L(v,!0),await e[d?"replace":"push"](T),E&&await V()}function z(u){const d=c.findIndex(B=>B.id===u);if(d===-1)return r.defaultRoute;const f=c[d+1],T=c[d-1],v=c.find(B=>B.id!==u),E=f||T||v;return E?E.to:r.defaultRoute}async function I(u=i.value,d={}){if(!u)return;if(!d.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&&d.redirect!==null,v=T?d.redirect??z(u):null;await U(u,{force:d.force}),d.redirect!==null&&T&&v&&await e.replace(v)}async function U(u,d={}){const f=c.findIndex(T=>T.id===u);f!==-1&&(c.splice(f,1),k.value===u&&(k.value=null),i.value===u&&(i.value=null,g.value=void 0))}async function L(u=i.value??void 0,d=!1){if(!u)return;const f=c.find(B=>B.id===u);if(!f)return;const T=r.keepAlive&&f.alive,v=`${u}::${f.renderKey??0}`;T&&(l.delete(v),f.alive=!1,await n.nextTick()),f.renderKey=(f.renderKey??0)+1;const E=`${u}::${f.renderKey}`;T&&(l.add(E),f.alive=!0),k.value=u,await n.nextTick(),d||await n.nextTick(),k.value=null}async function se(u=!1){for(const d of c)await L(d.id,u)}function w(u,d){const f=c.find(v=>v.id===u);if(!f)return;const T=`${u}::${f.renderKey??0}`;d?(l.add(T),f.alive=!0,oe(c,r.maxAlive,i.value,l)):(l.delete(T),f.alive=!1)}function X(u){const d=c.find(T=>T.id===u);if(!d)return;const f=`${u}::${d.renderKey??0}`;l.delete(f),d.alive=!1,d.renderKey=(d.renderKey??0)+1}function x(){l.clear(),c.forEach(u=>{u.alive=!1})}function S(){return m.value.slice()}async function Q(u=r.defaultRoute){c.splice(0,c.length),i.value=null,g.value=void 0;for(const d of r.initialTabs){const f=D(e,d.to),T=q(f,d,r.keepAlive);c.push(T)}await e.replace(u)}async function V(){const u=i.value;u&&await L(u,!0)}function R(u){return typeof u.matched=="object"?M(u):M(D(e,u))}function ce(){const u=c.find(d=>d.id===i.value);return{tabs:c.map(Me),active:u?u.to:null}}async function _(u){y=!0,c.splice(0,c.length),i.value=null,g.value=void 0;const d=u?.tabs??[];for(const T of d)try{const v=D(e,T.to),E=Le(T),B=q(v,E,r.keepAlive);ne(c,B,"last",null)}catch(v){console.warn("[RouterTabs] Failed to restore tab",T,v)}y=!1;const f=u?.active??d[d.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 d=P(u);i.value=d.id,g.value=d,oe(c,r.maxAlive,i.value,l)},{immediate:!0}),r.initialTabs.length&&r.initialTabs.forEach(u=>{const d=D(e,u.to),f=q(d,u,r.keepAlive);ne(c,f,"last",null)}),{options:r,tabs:c,activeId:i,current:g,includeKeys:m,refreshingKey:k,openTab:A,closeTab:I,removeTab:U,refreshTab:L,refreshAll:se,setTabAlive:w,evictCache:X,clearCache:x,getCacheKeys:S,reset:Q,reload:V,getRouteKey:R,matchRoute:$,snapshot:ce,hydrate:_,ensureTab:P}}function he(e){return e?typeof e=="string"?{name:e}:e:{}}const O=Symbol("RouterTabsContext"),W="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 ye(e,a,r){if(typeof document>"u")return;const{expiresInDays:c=7,path:i="/",domain:g,secure:k,sameSite:l="lax"}=r,m=[`${encodeURIComponent(e)}=${encodeURIComponent(a)}`];if(c!==1/0){const y=new Date(Date.now()+c*Ne).toUTCString();m.push(`Expires=${y}`)}i&&m.push(`Path=${i}`),g&&m.push(`Domain=${g}`),k&&m.push("Secure"),l&&m.push(`SameSite=${l.charAt(0).toUpperCase()}${l.slice(1)}`),document.cookie=m.join("; ")}function Te(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),ze=e=>{if(!e)return null;try{return JSON.parse(e)}catch{return null}};function ae(e={}){const{cookieKey:a=W,serialize:r=je,deserialize:c=ze}=e,i=re({optional:!0}),g=n.ref(!0),k=l=>{n.onMounted(async()=>{const m=c(Oe(a));if(m&&m.tabs?.length)try{if(g.value=!0,await l.hydrate(m),m.active){await n.nextTick();const $=l.tabs.find(P=>P.to===m.active);$&&(l.activeId.value=$.id,l.current.value=$)}}finally{g.value=!1}else if(Object.prototype.hasOwnProperty.call(e,"fallbackRoute"))try{g.value=!0;const $=e.fallbackRoute??l.options.defaultRoute;await l.reset($)}finally{g.value=!1}else g.value=!1;const y=l.snapshot();y.tabs.length?ye(a,r(y),e):Te(a,e),g.value=!1}),n.watch(()=>({tabs:l.tabs.map(m=>({to:m.to,title:m.title,tips:m.tips,icon:m.icon,tabClass:m.tabClass,closable:m.closable,renderKey:m.renderKey})),active:l.activeId.value}),()=>{if(g.value)return;const m=l.snapshot();m.tabs.length?ye(a,r(m),e):Te(a,e)},{deep:!0})};i?k(i):n.onMounted(()=>{const l=re({optional:!0});l&&k(l)})}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:W},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),k=n.computed(()=>!!r?.slots?.start),l=n.computed(()=>!!r?.slots?.end),m=n.ref(0),y=n.computed(()=>{m.value;const t={};return i.tabs.forEach(o=>{const s=typeof o.title=="string"?o.title:String(o.title||ue(o));t[o.id]=s}),t});function $(){m.value++}const P=new Map,A=new Map;function z(t,o){if(!(!o||P.has(t)))try{P.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 h=[];if(o.routeTabTitle!==void 0)try{const p=n.watch(()=>{const b=o.routeTabTitle;return b&&typeof b=="object"&&"value"in b?b.value:b},b=>{if(b!=null){const N=String(b);s.title=N,$()}},{immediate:!0});h.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabTitle for ${t}:`,p)}if(o.routeTabIcon!==void 0)try{const p=n.watch(()=>{const b=o.routeTabIcon;return b&&typeof b=="object"&&"value"in b?b.value:b},b=>{b!=null&&(s.icon=String(b),$())},{immediate:!0});h.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabIcon for ${t}:`,p)}if(o.routeTabClosable!==void 0)try{const p=n.watch(()=>{const b=o.routeTabClosable;return b&&typeof b=="object"&&"value"in b?b.value:b},b=>{b!=null&&(s.closable=!!b,$())},{immediate:!0});h.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabClosable for ${t}:`,p)}if(o.routeTabMeta!==void 0)try{const p=n.watch(()=>{const b=o.routeTabMeta;return b&&typeof b=="object"&&"value"in b?b.value:b},b=>{b&&typeof b=="object"&&(Object.assign(s,b),$())},{immediate:!0,deep:!0});h.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabMeta for ${t}:`,p)}A.set(t,h)}catch(s){console.error(`[RouterTab] Error in setupComponentWatching for ${t}:`,s),I(t)}}function I(t){try{const o=A.get(t);o&&(o.forEach(s=>{try{s()}catch(h){console.error(`[RouterTab] Error cleaning up watcher for ${t}:`,h)}}),A.delete(t)),P.delete(t)}catch(o){console.error(`[RouterTab] Error in cleanupComponentWatching for ${t}:`,o)}}function U(t,o){try{t?t.routeTabTitle!==void 0||t.routeTabIcon!==void 0||t.routeTabClosable!==void 0?z(o,t):t.$&&(t.$.routeTabTitle!==void 0||t.$.routeTabIcon!==void 0||t.$.routeTabClosable!==void 0)&&z(o,t.$):t===null&&I(o)}catch(s){console.error(`[RouterTab] Error handling component ref for ${o}:`,s),I(o)}}if(n.onErrorCaptured((t,o,s)=>(console.error("[RouterTab] Error captured from component:",t,s),!1)),e.cookieKey!==null||e.persistence){const t={...e.persistence??{}};e.cookieKey!==null?t.cookieKey=e.cookieKey||W:t.cookieKey||(t.cookieKey=W),ae(t)}const L=n.computed(()=>he(e.tabTransition)),se=n.computed(()=>he(e.pageTransition)),w=n.reactive({visible:!1,target:null,position:{x:0,y:0}}),X=n.ref(null),x=n.ref([]),S=n.ref(-1),Q=n.ref(null),V=new Map,R=n.reactive({dragging:!1,dragIndex:-1,dropIndex:-1,dragTab:null}),ce=["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 d(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 s=t.filter(h=>h.closable!==!1);if(s.length){for(const h of s)i.activeId.value===h.id?await i.closeTab(h.id,{redirect:o.to,force:!0}):await i.removeTab(h.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})=>fe(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(d(t),t)},enable:({target:t})=>d(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 E(){w.visible=!1,w.target=null,S.value=-1,x.value=[]}function B(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",E,{once:!0}),n.nextTick(()=>{ut()})}))}function ct(t,o){const s=typeof t=="string"?{id:t}:t,h=v[s.id],p=s.label??h?.label??String(s.id),b=s.visible??h?.visible??!0;if(!(typeof b=="function"?b(o):b!==!1))return null;const pe=s.enable??h?.enable??!0,It=typeof pe=="function"?pe(o):pe!==!1,Ae=s.handler??h?.handler;if(!Ae)return null;const xt=async()=>{await Promise.resolve(Ae(o))};return{id:String(s.id),label:p,disabled:!It,action:xt}}const F=n.computed(()=>{if(!w.visible||!w.target||e.contextmenu===!1)return[];const t=Array.isArray(e.contextmenu)?e.contextmenu:ce,o={target:w.target,controller:i};return t.map(s=>ct(s,o)).filter(s=>!!s)});function ut(){const t=X.value;if(!t)return;const o=8,{innerWidth:s,innerHeight:h}=window,p=t.getBoundingClientRect();let b=w.position.x,N=w.position.y;p.right>s-o&&(b=Math.max(o,s-p.width-o)),p.bottom>h-o&&(N=Math.max(o,h-p.height-o)),(b!==w.position.x||N!==w.position.y)&&(w.position.x=b,w.position.y=N)}function dt(t,o){x.value[o]=t??null}function ft(t){if(t<0)return;x.value[t]?.focus({preventScroll:!0})}function Z(t,o,s=F.value){if(!s.length)return-1;const h=s.length;let p=t;for(let b=0;b<h;b++)if(p=(p+o+h)%h,!s[p].disabled)return p;return-1}function Y(t){S.value=t,!(t<0)&&n.nextTick(()=>ft(t))}function Ke(t){const o=Z(S.value,t);o!==-1&&Y(o)}function bt(t){if(!w.visible)return;const o=t.key,s=F.value;if(!s.length)return;if(o==="Tab"){E();return}if(["ArrowDown","ArrowUp","ArrowRight","ArrowLeft","Home","End","Enter"," ","Spacebar","Escape"].includes(o))switch(t.preventDefault(),o){case"ArrowDown":case"ArrowRight":Ke(1);break;case"ArrowUp":case"ArrowLeft":Ke(-1);break;case"Home":Y(Z(-1,1));break;case"End":Y(Z(s.length,-1));break;case"Enter":case" ":case"Spacebar":{const p=S.value;if(p>-1){const b=s[p];b.disabled||$e(b)}break}case"Escape":E();break}}async function $e(t){t.disabled||(E(),await t.action())}function ue(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 mt(t){return y.value[t.id]||ue(t)}function pt(t,o){return n.defineComponent({name:o,setup(s,{attrs:h,slots:p}){return()=>n.h(t,h,p)}})}const de=new Map,Pe=n.ref(0);function gt(t,o,s){de.has(s)||(de.set(s,o),Pe.value++),t&&U(t,s)}function ht(t,o){return t&&((!t.name||t.name!==o)&&(t.name=o),t)}function yt(t){const o=i.getRouteKey(t),s=c.currentRoute.value,h=i.getRouteKey(s);return o===h}function Be(t){const o=i.getRouteKey(t),s=i.tabs.find(b=>b.id===o);if(!s)return console.warn("[RouterTab] Tab not found for route:",o),`${o}::0`;const h=s.renderKey??0,p=`${o}::${h}`;return(o.includes("students")||o.includes("classroom")||o.includes("quiz"))&&console.log(`[getComponentCacheKey] Route: ${t.fullPath}`,{routeKey:o,renderKey:h,cacheKey:p,tabAlive:s.alive,includeKeys:me.value,isIncluded:me.value.includes(p)}),p}function Tt(t){return`${Be(t)}::refresh`}function fe(t){return!(t.closable===!1||i.options.keepLastTab&&i.tabs.length<=1)}async function vt(t){await i.closeTab(t.id)}function kt(t,o){o?V.set(t,o):V.delete(t)}function be(t){n.nextTick(()=>{const o=V.get(t),s=Q.value;if(o&&s){const h=o.getBoundingClientRect(),p=s.getBoundingClientRect();(h.left<p.left||h.right>p.right)&&o.scrollIntoView({behavior:"smooth",block:"nearest",inline:"nearest"})}})}function wt(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),be(t.id))}function Rt(t){return["router-tab__item",{"is-active":i.activeId.value===t.id,"is-closable":fe(t),"is-dragging":R.dragging&&R.dragTab?.id===t.id,"is-drag-over":R.dropIndex===_(t.id)},t.tabClass]}function Ct(t){return i.refreshingKey.value===i.getRouteKey(t)}function Et(t){const o=i.getRouteKey(t),s=i.tabs.find(h=>h.id===o);return s?s.alive:!1}function Kt(t){const o=i.getRouteKey(t);return!!i.tabs.find(h=>h.id===o)}function $t(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 Pt(t,o){!e.sortable||!R.dragging||(o.preventDefault(),o.dataTransfer&&(o.dataTransfer.dropEffect="move"))}function Bt(t){!e.sortable||!R.dragging||(R.dropIndex=t)}function St(){!e.sortable||R.dragging}function At(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",E)}),n.onBeforeUnmount(()=>{document.removeEventListener("keydown",E),r.appContext.config.globalProperties.$tabs=null,A.forEach(t=>{t.forEach(o=>{try{o()}catch(s){console.error("[RouterTab] Error during cleanup:",s)}})}),A.clear(),P.clear()}),n.watch(()=>e.keepAlive,t=>{i.options.keepAlive=t}),n.watch(()=>i.activeId.value,t=>{t&&be(t),E()}),n.watch(()=>i.tabs.length,()=>{const t=new Set(i.tabs.map(s=>s.id));Array.from(P.keys()).forEach(s=>{t.has(s)||(console.log(`[RouterTab] Cleaning up stale component instance: ${s}`),I(s))})}),n.watch(()=>e.contextmenu,t=>{t||E()}),n.watch(()=>F.value.length,t=>{w.visible&&t===0&&E()},{flush:"post"}),n.watch(F,t=>{if(!w.visible)return;x.value=new Array(t.length).fill(null);const o=Z(-1,1,t);Y(o)},{flush:"post"}),n.watch(()=>w.visible,t=>{t||(S.value=-1,x.value=[])});const me=i.includeKeys;return{controller:i,tabs:i.tabs,includeKeys:me,componentCache:de,componentCacheTrigger:Pe,cacheCurrentComponent:gt,tabTransitionProps:L,pageTransitionProps:se,buildTabClass:Rt,activate:wt,close:vt,context:w,menuItems:F,handleMenuAction:$e,showContextMenu:B,hideContextMenu:E,getTabTitle:ue,isClosable:fe,isRefreshing:Ct,isTabCached:Et,isTabReady:Kt,hasCustomSlot:g,hasStartSlot:k,hasEndSlot:l,onDragStart:$t,onDragOver:Pt,onDragEnter:Bt,onDragLeave:St,onDrop:At,onDragEnd:Se,setupComponentWatching:z,cleanupComponentWatching:I,handleComponentRef:U,getReactiveTabTitle:mt,getComponentCacheKey:Be,getRefreshComponentKey:Tt,createNamedComponent:pt,ensureNamedComponent:ht,shouldRenderRoute:yt,triggerTabUpdate:$,menuRef:X,highlightedIndex:S,setMenuItemRef:dt,onMenuKeydown:bt,highlightMenuIndex:Y,scrollContainer:Q,setTabRef:kt,scrollTabIntoView:be}}}),_e=(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"},qe={class:"router-tab__scroll",ref:"scrollContainer"},We=["data-title","draggable","onClick","onAuxclick","onContextmenu","onDragstart","onDragover","onDragenter","onDrop"],He=["title"],Je=["onClick"],Ge={class:"router-tab__container"},Xe=["aria-disabled","disabled","tabindex","onMouseenter","onClick"];function Qe(e,a,r,c,i,g){const k=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",qe,[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,m)=>(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:y=>e.setTabRef(l.id,y),onClick:y=>e.activate(l),onAuxclick:n.withModifiers(y=>e.close(l),["middle","prevent"]),onContextmenu:n.withModifiers(y=>e.showContextMenu(l,y),["prevent"]),onDragstart:y=>e.onDragStart(l,m,y),onDragover:y=>e.onDragOver(m,y),onDragenter:y=>e.onDragEnter(m),onDragleave:a[0]||(a[0]=(...y)=>e.onDragLeave&&e.onDragLeave(...y)),onDrop:y=>e.onDrop(m,y),onDragend:a[1]||(a[1]=(...y)=>e.onDragEnd&&e.onDragEnd(...y))},[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,He),e.isClosable(l)?(n.openBlock(),n.createElementBlock("a",{key:1,class:"router-tab__item-close",onClick:n.withModifiers(y=>e.close(l),["stop"])},null,8,Je)):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,[n.createVNode(k,null,{default:n.withCtx(({Component:l,route:m})=>[e.hasCustomSlot?n.renderSlot(e.$slots,"default",n.normalizeProps(n.mergeProps({key:0},{Component:l,route:m,controller:e.controller,pageRef:y=>e.handleComponentRef(y,e.controller.getRouteKey(m))}))):(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.ensureNamedComponent(l,e.controller.getRouteKey(m))),{key:e.controller.getRouteKey(m),ref:y=>e.handleComponentRef(y,e.controller.getRouteKey(m)),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(m),ref:y=>e.handleComponentRef(y,e.controller.getRouteKey(m)),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,m)=>(n.openBlock(),n.createElementBlock("a",{key:l.id,role:"menuitem",class:n.normalizeClass(["router-tab__contextmenu-item",{"is-focused":m===e.highlightedIndex}]),"aria-disabled":l.disabled,disabled:l.disabled,tabindex:l.disabled?-1:m===e.highlightedIndex?0:-1,ref_for:!0,ref:y=>e.setMenuItemRef(y,m),onMouseenter:y=>!l.disabled&&e.highlightMenuIndex(m),onClick:y=>e.handleMenuAction(l)},n.toDisplayString(l.label),43,Xe))),128))],36),[[n.vShow,e.context.visible&&e.context.target]])])}const ie=_e(Ue,[["render",Qe]]),Ze={class:"router-tabs","aria-hidden":"true"},H=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",Ze))}}),ve="tab-theme-style",ke="tab-theme-primary-color",et="system",we="(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"},tt={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 nt(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??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 Re(e){if(typeof document>"u")return;const a=document.documentElement,r=window.matchMedia(we),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 Ce(e={}){if(typeof window>"u")return;const{styleKey:a=ve,primaryKey:r=ke,defaultStyle:c=et,defaultPrimary:i}=e,g=window.localStorage.getItem(a)??c;Re(g);const l=g==="dark"||g==="system"&&window.matchMedia(we).matches?{...tt}:{...K};i&&(l.primary=i);const m=nt(r);le(m?{...l,...m}:l)}function ot(e,a){if(typeof window>"u")return;const r=a?.styleKey??ve;window.localStorage.setItem(r,e),Re(e)}function rt(e,a){if(typeof window>"u")return;const r=a?.primaryKey??ke;window.localStorage.setItem(r,JSON.stringify(e)),le(e)}function J(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 G(e={}){const a=J(e.title,"Untitled"),r=J(e.icon,""),c=J(e.closable,!0),i=J(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 at(e,a="Page"){return G({title:n.computed(()=>e.value?"Loading...":a),icon:n.computed(()=>e.value?"mdi-loading mdi-spin":"mdi-page"),closable:n.computed(()=>!e.value)})}function it(e,a="Page",r="mdi-page"){return G({title:n.computed(()=>e.value>0?`${a} (${e.value})`:a),icon:n.computed(()=>e.value>0?"mdi-bell-badge":r)})}function lt(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 G({title:n.computed(()=>a+r[e.value].suffix),icon:n.computed(()=>r[e.value].icon),closable:n.computed(()=>e.value!=="loading")})}let Ee=!1;const st={install(e,a){if(Ee)return;Ee=!0;const{initTheme:r=!0,themeOptions:c,componentName:i=ie.name||"RouterTab",tabsComponentName:g=H.name||"RouterTabs"}=a??{};r&&Ce(c??{}),e.component(i,ie),e.component(g,H),g.toLowerCase()!=="router-tabs"&&e.component("router-tabs",H),Object.defineProperty(e.config.globalProperties,"$tabs",{configurable:!0,enumerable:!1,get(){return e._context.provides[O]},set(k){k&&e.provide(O,k)}})}};C.RouterTab=ie,C.RouterTabs=H,C.default=st,C.initRouterTabsTheme=Ce,C.routerTabsKey=O,C.setRouterTabsPrimary=rt,C.setRouterTabsTheme=ot,C.useLoadingTab=at,C.useNotificationTab=it,C.useReactiveTab=G,C.useRouterTabs=re,C.useRouterTabsPersistence=ae,C.useStatusTab=lt,Object.defineProperties(C,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
@@ -53,50 +53,40 @@
53
53
  </header>
54
54
 
55
55
  <div class="router-tab__container">
56
- <RouterView v-slot="routerSlot">
56
+ <RouterView v-slot="{ Component, route }">
57
57
  <template v-if="hasCustomSlot">
58
58
  <slot
59
59
  v-bind="{
60
- ...routerSlot,
60
+ Component,
61
+ route,
61
62
  controller,
62
- // Expose a ref binder so custom slots can keep reactivity
63
- pageRef: (el: any) => handleComponentRef(el, controller.getRouteKey(routerSlot.route))
63
+ pageRef: (el: any) => handleComponentRef(el, controller.getRouteKey(route))
64
64
  }"
65
65
  />
66
66
  </template>
67
67
  <template v-else>
68
- <transition
69
- v-bind="pageTransitionProps"
70
- appear
71
- >
72
- <component
73
- v-if="isRefreshing(routerSlot.route)"
74
- :is="routerSlot.Component"
75
- :key="getRefreshComponentKey(routerSlot.route)"
76
- :ref="(el: any) => handleComponentRef(el, controller.getRouteKey(routerSlot.route))"
77
- class="router-tab-page"
78
- />
79
- <KeepAlive
80
- v-else-if="controller.options.keepAlive"
81
- :include="includeKeys"
82
- :max="controller.options.maxAlive || undefined"
83
- >
68
+ <template v-if="controller.options.keepAlive">
69
+ <KeepAlive :include="includeKeys">
84
70
  <component
85
- v-if="isTabReady(routerSlot.route)"
86
- :is="routerSlot.Component"
87
- :key="getComponentCacheKey(routerSlot.route)"
88
- :ref="(el: any) => handleComponentRef(el, controller.getRouteKey(routerSlot.route))"
71
+ v-if="Component"
72
+ :is="ensureNamedComponent(Component, controller.getRouteKey(route))"
73
+ :key="controller.getRouteKey(route)"
74
+ :ref="(el: any) => handleComponentRef(el, controller.getRouteKey(route))"
89
75
  class="router-tab-page"
90
76
  />
91
77
  </KeepAlive>
92
- <component
93
- v-else-if="isTabReady(routerSlot.route)"
94
- :is="routerSlot.Component"
95
- :key="getComponentCacheKey(routerSlot.route)"
96
- :ref="(el: any) => handleComponentRef(el, controller.getRouteKey(routerSlot.route))"
97
- class="router-tab-page"
98
- />
99
- </transition>
78
+ </template>
79
+ <template v-else>
80
+ <transition v-bind="pageTransitionProps" appear>
81
+ <component
82
+ v-if="Component"
83
+ :is="Component"
84
+ :key="controller.getRouteKey(route)"
85
+ :ref="(el: any) => handleComponentRef(el, controller.getRouteKey(route))"
86
+ class="router-tab-page"
87
+ />
88
+ </transition>
89
+ </template>
100
90
  </template>
101
91
  </RouterView>
102
92
  </div>
@@ -133,6 +123,7 @@ import {
133
123
  computed,
134
124
  defineComponent,
135
125
  getCurrentInstance,
126
+ h,
136
127
  nextTick,
137
128
  onBeforeUnmount,
138
129
  onErrorCaptured,
@@ -435,23 +426,8 @@ export default defineComponent({
435
426
  onErrorCaptured((err, instance, info) => {
436
427
  console.error('[RouterTab] Error captured from component:', err, info)
437
428
 
438
- // Try to find the route key for the erroring component
439
- if (instance && controller.activeId.value) {
440
- const routeKey = controller.activeId.value
441
-
442
- // Clean up the component instance to prevent stale state
443
- cleanupComponentWatching(routeKey)
444
-
445
- // Remove from KeepAlive cache if it's cached
446
- const tab = controller.tabs.find(t => t.id === routeKey)
447
- if (tab && tab.alive) {
448
- console.warn(`[RouterTab] Removing errored component ${routeKey} from KeepAlive cache`)
449
- tab.alive = false
450
- nextTick(() => {
451
- tab.alive = true
452
- })
453
- }
454
- }
429
+ // Just log the error, don't try to manipulate the cache
430
+ // as it can cause infinite loops with KeepAlive
455
431
 
456
432
  // Return false to propagate the error to parent
457
433
  return false
@@ -798,21 +774,106 @@ export default defineComponent({
798
774
  return reactiveTitles[tab.id] || getTabTitle(tab)
799
775
  }
800
776
 
777
+ /**
778
+ * Creates a wrapper component with a specific name for KeepAlive caching.
779
+ * This is necessary because KeepAlive's include prop matches component names.
780
+ */
781
+ function createNamedComponent(component: any, name: string) {
782
+ return defineComponent({
783
+ name,
784
+ setup(_, { attrs, slots }) {
785
+ return () => h(component, attrs, slots)
786
+ }
787
+ })
788
+ }
789
+
790
+ // Cache for component references from RouterView (non-reactive to avoid circular reference issues)
791
+ const componentCache = new Map<string, any>()
792
+ const componentCacheTrigger = ref(0) // Trigger reactivity manually
793
+
794
+ /**
795
+ * Cache the current component without triggering infinite loops.
796
+ * Called from ref callback, only caches once per route key.
797
+ */
798
+ function cacheCurrentComponent(el: any, component: any, key: string): void {
799
+ if (!componentCache.has(key)) {
800
+ componentCache.set(key, component)
801
+ componentCacheTrigger.value++
802
+ }
803
+ // Always handle the component ref to set up watchers
804
+ if (el) {
805
+ handleComponentRef(el, key)
806
+ }
807
+ }
808
+
809
+ /**
810
+ * Ensures a component has a name for KeepAlive to cache properly.
811
+ * Assigns the cache key as the component's name directly.
812
+ */
813
+ function ensureNamedComponent(component: any, cacheKey: string) {
814
+ if (!component) return component
815
+
816
+ // Directly assign the name to the component
817
+ // This mutates the component but is necessary for KeepAlive matching
818
+ if (!component.name || component.name !== cacheKey) {
819
+ component.name = cacheKey
820
+ }
821
+
822
+ return component
823
+ }
824
+
825
+ /**
826
+ * Determines if a route should be rendered.
827
+ * Used to control which component is visible within KeepAlive.
828
+ */
829
+ function shouldRenderRoute(route: RouteLocationNormalizedLoaded): boolean {
830
+ const currentRouteKey = controller.getRouteKey(route)
831
+ const currentRoute = router.currentRoute.value
832
+ const activeRouteKey = controller.getRouteKey(currentRoute)
833
+
834
+ return currentRouteKey === activeRouteKey
835
+ }
836
+
837
+ /**
838
+ * Generates the KeepAlive cache key for a component.
839
+ * Format: `{routeKey}::{renderKey}`
840
+ *
841
+ * Example: '/quiz-results::0' (first mount), '/quiz-results::1' (after refresh)
842
+ *
843
+ * The renderKey is incremented on manual refresh to force component recreation.
844
+ * Stable renderKey during navigation allows KeepAlive to preserve component state.
845
+ */
801
846
  function getComponentCacheKey(route: RouteLocationNormalizedLoaded): string {
802
847
  const routeKey = controller.getRouteKey(route)
803
848
  const tab = controller.tabs.find(item => item.id === routeKey)
804
849
 
805
- // If tab doesn't exist yet, ensure it's created
806
850
  if (!tab) {
807
- // This shouldn't happen, but handle it gracefully
808
851
  console.warn('[RouterTab] Tab not found for route:', routeKey)
809
852
  return `${routeKey}::0`
810
853
  }
811
854
 
812
855
  const renderKey = tab.renderKey ?? 0
813
- return `${routeKey}::${renderKey}`
856
+ const cacheKey = `${routeKey}::${renderKey}`
857
+
858
+ // Debug logging for specific routes
859
+ if (routeKey.includes('students') || routeKey.includes('classroom') || routeKey.includes('quiz')) {
860
+ console.log(`[getComponentCacheKey] Route: ${route.fullPath}`, {
861
+ routeKey,
862
+ renderKey,
863
+ cacheKey,
864
+ tabAlive: tab.alive,
865
+ includeKeys: includeKeys.value,
866
+ isIncluded: includeKeys.value.includes(cacheKey)
867
+ })
868
+ }
869
+
870
+ return cacheKey
814
871
  }
815
872
 
873
+ /**
874
+ * Generates a special key for components in refreshing state.
875
+ * Appends '::refresh' to ensure it's treated as separate from cached instance.
876
+ */
816
877
  function getRefreshComponentKey(route: RouteLocationNormalizedLoaded): string {
817
878
  return `${getComponentCacheKey(route)}::refresh`
818
879
  }
@@ -894,14 +955,21 @@ export default defineComponent({
894
955
  return controller.refreshingKey.value === controller.getRouteKey(route)
895
956
  }
896
957
 
958
+ function isTabCached(route: RouteLocationNormalizedLoaded) {
959
+ const routeKey = controller.getRouteKey(route)
960
+ const tab = controller.tabs.find(tab => tab.id === routeKey)
961
+ return tab ? tab.alive : false
962
+ }
963
+
897
964
  function isTabReady(route: RouteLocationNormalizedLoaded) {
898
965
  const routeKey = controller.getRouteKey(route)
899
966
  const tab = controller.tabs.find(tab => tab.id === routeKey)
967
+
900
968
  if (!tab) return false
901
969
 
902
- // If KeepAlive is enabled, only render if tab is marked as alive (included in cache)
903
- // If KeepAlive is disabled, render if tab exists
904
- return controller.options.keepAlive ? tab.alive : true
970
+ // Always return true - let KeepAlive handle the caching logic
971
+ // The tab exists in the tabs array, so it should be rendered
972
+ return true
905
973
  }
906
974
 
907
975
  // Drag and drop handlers
@@ -1060,6 +1128,9 @@ export default defineComponent({
1060
1128
  controller,
1061
1129
  tabs: controller.tabs,
1062
1130
  includeKeys,
1131
+ componentCache,
1132
+ componentCacheTrigger,
1133
+ cacheCurrentComponent,
1063
1134
  tabTransitionProps,
1064
1135
  pageTransitionProps,
1065
1136
  buildTabClass,
@@ -1073,6 +1144,7 @@ export default defineComponent({
1073
1144
  getTabTitle,
1074
1145
  isClosable,
1075
1146
  isRefreshing,
1147
+ isTabCached,
1076
1148
  isTabReady,
1077
1149
  hasCustomSlot,
1078
1150
  hasStartSlot,
@@ -1089,6 +1161,9 @@ export default defineComponent({
1089
1161
  getReactiveTabTitle,
1090
1162
  getComponentCacheKey,
1091
1163
  getRefreshComponentKey,
1164
+ createNamedComponent,
1165
+ ensureNamedComponent,
1166
+ shouldRenderRoute,
1092
1167
  triggerTabUpdate,
1093
1168
  menuRef,
1094
1169
  highlightedIndex,