vue3-router-tab 1.3.9 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -8
- package/dist/vue3-router-tab.css +1 -1
- package/dist/vue3-router-tab.js +555 -553
- package/dist/vue3-router-tab.umd.cjs +1 -1
- package/lib/components/RouterTab.vue +18 -2
- package/lib/core/createRouterTabs.ts +11 -23
- package/lib/scss/index.scss +1 -2
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
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"}})}));
|
|
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"}})}));
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
<KeepAlive :include="includeKeys">
|
|
70
70
|
<component
|
|
71
71
|
v-if="Component"
|
|
72
|
-
:is="
|
|
73
|
-
:key="
|
|
72
|
+
:is="getNamedComponent(Component, getComponentCacheKey(route))"
|
|
73
|
+
:key="isRefreshing(route) ? getRefreshComponentKey(route) : getComponentCacheKey(route)"
|
|
74
74
|
:ref="(el: any) => handleComponentRef(el, controller.getRouteKey(route))"
|
|
75
75
|
class="router-tab-page"
|
|
76
76
|
/>
|
|
@@ -822,6 +822,21 @@ export default defineComponent({
|
|
|
822
822
|
return component
|
|
823
823
|
}
|
|
824
824
|
|
|
825
|
+
/**
|
|
826
|
+
* Returns a component wrapper with a stable name for KeepAlive include matching.
|
|
827
|
+
* Caches the wrapper per cacheKey to avoid recreating components on each render.
|
|
828
|
+
*/
|
|
829
|
+
function getNamedComponent(component: any, cacheKey: string) {
|
|
830
|
+
if (!component) return component
|
|
831
|
+
const cached = componentCache.get(cacheKey)
|
|
832
|
+
if (cached) return cached
|
|
833
|
+
|
|
834
|
+
const wrapped = createNamedComponent(component, cacheKey)
|
|
835
|
+
componentCache.set(cacheKey, wrapped)
|
|
836
|
+
componentCacheTrigger.value++
|
|
837
|
+
return wrapped
|
|
838
|
+
}
|
|
839
|
+
|
|
825
840
|
/**
|
|
826
841
|
* Determines if a route should be rendered.
|
|
827
842
|
* Used to control which component is visible within KeepAlive.
|
|
@@ -1163,6 +1178,7 @@ export default defineComponent({
|
|
|
1163
1178
|
getRefreshComponentKey,
|
|
1164
1179
|
createNamedComponent,
|
|
1165
1180
|
ensureNamedComponent,
|
|
1181
|
+
getNamedComponent,
|
|
1166
1182
|
shouldRenderRoute,
|
|
1167
1183
|
triggerTabUpdate,
|
|
1168
1184
|
menuRef,
|
|
@@ -234,19 +234,6 @@ export function createRouterTabs(
|
|
|
234
234
|
// Generate the current cache key for this tab
|
|
235
235
|
const currentCacheKey = `${key}::${tab.renderKey}`
|
|
236
236
|
|
|
237
|
-
// Debug logging for specific routes
|
|
238
|
-
if (key.includes('students') || key.includes('classroom') || key.includes('quiz')) {
|
|
239
|
-
console.log(`[ensureTab] EXISTING tab: ${route.fullPath}`, {
|
|
240
|
-
key,
|
|
241
|
-
shouldBeAlive,
|
|
242
|
-
currentRenderKey: tab.renderKey,
|
|
243
|
-
currentCacheKey,
|
|
244
|
-
isInCache: aliveCache.has(currentCacheKey),
|
|
245
|
-
aliveCacheSize: aliveCache.size,
|
|
246
|
-
cacheContents: Array.from(aliveCache)
|
|
247
|
-
})
|
|
248
|
-
}
|
|
249
|
-
|
|
250
237
|
// Manage KeepAlive cache state
|
|
251
238
|
if (shouldBeAlive) {
|
|
252
239
|
// Check if tab's current key is in the cache
|
|
@@ -254,16 +241,15 @@ export function createRouterTabs(
|
|
|
254
241
|
// Tab was evicted or never added to cache - add it back
|
|
255
242
|
aliveCache.add(currentCacheKey)
|
|
256
243
|
tab.alive = true
|
|
257
|
-
if (key.includes('students') || key.includes('classroom') || key.includes('quiz')) {
|
|
258
|
-
console.log(`[ensureTab] ✅ Added to cache: ${currentCacheKey}`)
|
|
259
|
-
}
|
|
260
244
|
} else if (!tab.alive) {
|
|
261
245
|
// Tab is in cache but marked as not alive - just reactivate
|
|
262
246
|
tab.alive = true
|
|
263
|
-
|
|
264
|
-
console.log(`[ensureTab] ✅ Reactivated: ${currentCacheKey}`)
|
|
265
|
-
}
|
|
247
|
+
|
|
266
248
|
}
|
|
249
|
+
} else if (tab.alive) {
|
|
250
|
+
// Route no longer wants to be cached; drop from cache and mark inactive
|
|
251
|
+
aliveCache.delete(currentCacheKey)
|
|
252
|
+
tab.alive = false
|
|
267
253
|
}
|
|
268
254
|
|
|
269
255
|
Object.assign(tab, pickMeta(route))
|
|
@@ -277,10 +263,6 @@ export function createRouterTabs(
|
|
|
277
263
|
if (tab.alive) {
|
|
278
264
|
const cacheKey = `${key}::${tab.renderKey ?? 0}`
|
|
279
265
|
aliveCache.add(cacheKey)
|
|
280
|
-
|
|
281
|
-
if (key.includes('students') || key.includes('classroom') || key.includes('quiz')) {
|
|
282
|
-
console.log(`[ensureTab] NEW tab created and cached: ${cacheKey}`)
|
|
283
|
-
}
|
|
284
266
|
}
|
|
285
267
|
|
|
286
268
|
insertTab(tabs, tab, options.appendPosition, activeId.value)
|
|
@@ -345,6 +327,12 @@ export function createRouterTabs(
|
|
|
345
327
|
const index = tabs.findIndex(item => item.id === id)
|
|
346
328
|
if (index === -1) return
|
|
347
329
|
|
|
330
|
+
const tab = tabs[index]
|
|
331
|
+
// Remove KeepAlive cache entry if present
|
|
332
|
+
const cacheKey = `${id}::${tab.renderKey ?? 0}`
|
|
333
|
+
aliveCache.delete(cacheKey)
|
|
334
|
+
tab.alive = false
|
|
335
|
+
|
|
348
336
|
tabs.splice(index, 1)
|
|
349
337
|
|
|
350
338
|
if (refreshingKey.value === id) {
|
package/lib/scss/index.scss
CHANGED
|
@@ -131,8 +131,7 @@
|
|
|
131
131
|
color: var(--router-tab-text);
|
|
132
132
|
font-size: var(--router-tab-font-size);
|
|
133
133
|
background-color: transparent;
|
|
134
|
-
border:
|
|
135
|
-
border-right: 1px solid var(--router-tab-border);
|
|
134
|
+
border: 1px solid var(--router-tab-border);
|
|
136
135
|
cursor: pointer;
|
|
137
136
|
user-select: none;
|
|
138
137
|
transition: var(--router-tab-transition);
|