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