vue3-router-tab 1.3.8 → 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/index.d.ts +25 -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"}})}));
|
package/index.d.ts
CHANGED
|
@@ -15,6 +15,28 @@ import type {
|
|
|
15
15
|
} from './lib/core/types'
|
|
16
16
|
import type { ColorStyle, RouterTabsThemeOptions } from './lib/theme'
|
|
17
17
|
|
|
18
|
+
export interface RouterTabsPluginOptions {
|
|
19
|
+
/**
|
|
20
|
+
* Whether to initialise the theme system automatically during install.
|
|
21
|
+
* Defaults to `true`.
|
|
22
|
+
*/
|
|
23
|
+
initTheme?: boolean
|
|
24
|
+
/**
|
|
25
|
+
* Theme options passed to `initRouterTabsTheme` when `initTheme` is enabled.
|
|
26
|
+
*/
|
|
27
|
+
themeOptions?: RouterTabsThemeOptions
|
|
28
|
+
/**
|
|
29
|
+
* Global component name used when registering `RouterTab`.
|
|
30
|
+
* Defaults to the component's `name` option or `"RouterTab"`.
|
|
31
|
+
*/
|
|
32
|
+
componentName?: string
|
|
33
|
+
/**
|
|
34
|
+
* Global component name used when registering `RouterTabs`.
|
|
35
|
+
* Defaults to the component's `name` option or `"RouterTabs"`.
|
|
36
|
+
*/
|
|
37
|
+
tabsComponentName?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
18
40
|
export type {
|
|
19
41
|
TabRecord,
|
|
20
42
|
TabInput,
|
|
@@ -95,7 +117,9 @@ export declare const RouterTab: DefineComponent<{
|
|
|
95
117
|
persistence: RouterTabsPersistenceOptions | null
|
|
96
118
|
}>
|
|
97
119
|
|
|
98
|
-
export interface RouterTabPlugin
|
|
120
|
+
export interface RouterTabPlugin {
|
|
121
|
+
install: (app: App, options?: RouterTabsPluginOptions) => void
|
|
122
|
+
}
|
|
99
123
|
|
|
100
124
|
declare const plugin: RouterTabPlugin
|
|
101
125
|
|
|
@@ -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);
|