vue3-router-tab 1.3.4 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- (function(w,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):(w=typeof globalThis<"u"?globalThis:w||self,n(w["vue3-router-tab"]={},w.Vue,w.VueRouter))})(this,(function(w,n,Re){"use strict";function Ee(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,o){const a=e.resolve(o);if(!a||!a.matched.length)throw new Error(`[RouterTabs] Unable to resolve route: ${String(o)}`);return a}const Pe={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 $(e){const o=e.meta?.key;if(typeof o=="function"){const a=o(e);if(typeof a=="string"&&a.length)return a}else if(typeof o=="string"&&o.length){const a=Pe[o.toLowerCase()];return a?a(e):o}return e.fullPath}function G(e,o){const a=e.meta?.keepAlive;return typeof a=="boolean"?a:o}function X(e,o){const a=e.meta?.reuse;return typeof a=="boolean"?a:o}function se(e){const o=e.meta??{},a={};return"title"in o&&(a.title=o.title),"tips"in o&&(a.tips=o.tips),"icon"in o&&(a.icon=o.icon),"closable"in o&&(a.closable=o.closable),"tabClass"in o&&(a.tabClass=o.tabClass),"target"in o&&(a.target=o.target),"href"in o&&(a.href=o.href),a}function _(e,o,a){const s=se(e);return{id:$(e),to:e.fullPath,fullPath:e.fullPath,matched:e,alive:G(e,a),reusable:X(e,!1),closable:s.closable??!0,renderKey:typeof o.renderKey=="number"?o.renderKey:0,...s,...o}}function Q(e,o,a,s){if(!e.find(p=>p.id===o.id)){if(a==="next"&&s){const p=e.findIndex(v=>v.id===s);if(p!==-1){e.splice(p+1,0,o);return}}e.push(o)}}function ce(e,o,a){if(!o||o<=0)return;const s=e.filter(i=>i.alive);for(;s.length>o;){const i=s.shift();if(!i||i.id===a)continue;const p=e.findIndex(v=>v.id===i.id);p>-1&&(e[p].alive=!1)}}function Be(e){return{to:e.to,title:e.title,tips:e.tips,icon:e.icon,tabClass:e.tabClass,closable:e.closable,renderKey:e.renderKey}}function Ke(e){const o={};return"title"in e&&(o.title=e.title),"tips"in e&&(o.tips=e.tips),"icon"in e&&(o.icon=e.icon),"tabClass"in e&&(o.tabClass=e.tabClass),"closable"in e&&(o.closable=e.closable),"renderKey"in e&&typeof e.renderKey=="number"&&(o.renderKey=e.renderKey),o}function Ae(e,o={}){const a=Ee(o),s=n.reactive([]),i=n.ref(null),p=n.shallowRef(),v=n.ref(null),c=n.computed(()=>s.filter(l=>l.alive).map(l=>`${l.id}::${l.renderKey??0}`));let d=!1;function g(l){const m=typeof l.matched=="object"?l:x(e,l);return{key:$(m),fullPath:m.fullPath,alive:G(m,a.keepAlive),reusable:X(m,!1),matched:m}}function E(l){const m=$(l);let b=s.find(k=>k.id===m);return b?(b.fullPath=l.fullPath,b.to=l.fullPath,b.matched=l,b.alive=G(l,a.keepAlive),b.reusable=X(l,b.reusable),typeof b.renderKey!="number"&&(b.renderKey=0),Object.assign(b,se(l)),b):(b=_(l,{},a.keepAlive),Q(s,b,a.appendPosition,i.value),ce(s,a.maxAlive,i.value),b)}async function A(l,m=!1,b=!0){const k=x(e,l),C=$(k),P=i.value===C;b==="sameTab"&&(b=P),b&&await M(C,!0),await e[m?"replace":"push"](k),P&&await T()}function S(l){const m=s.findIndex(K=>K.id===l);if(m===-1)return a.defaultRoute;const b=s[m+1],k=s[m-1],C=s.find(K=>K.id!==l),P=b||k||C;return P?P.to:a.defaultRoute}async function N(l=i.value,m={}){if(!l)return;if(!m.force&&a.keepLastTab&&s.length===1)throw new Error("[RouterTabs] Unable to close the final tab when keepLastTab is true.");const k=i.value===l&&m.redirect!==null,C=k?m.redirect??S(l):null;await O(l,{force:m.force}),m.redirect!==null&&k&&C&&await e.replace(C)}async function O(l,m={}){const b=s.findIndex(k=>k.id===l);b!==-1&&(s.splice(b,1),v.value===l&&(v.value=null),i.value===l&&(i.value=null,p.value=void 0))}async function M(l=i.value??void 0,m=!1){if(!l)return;const b=s.find(C=>C.id===l);if(!b)return;const k=a.keepAlive&&b.alive;k&&(b.alive=!1,await n.nextTick()),b.renderKey=(b.renderKey??0)+1,k&&(b.alive=!0),v.value=l,await n.nextTick(),m||await n.nextTick(),v.value=null}async function oe(l=!1){for(const m of s)await M(m.id,l)}async function ae(l=a.defaultRoute){s.splice(0,s.length),i.value=null,p.value=void 0;for(const m of a.initialTabs){const b=x(e,m.to),k=_(b,m,a.keepAlive);s.push(k)}await e.replace(l)}async function T(){const l=i.value;l&&await M(l,!0)}function W(l){return typeof l.matched=="object"?$(l):$(x(e,l))}function D(){const l=s.find(m=>m.id===i.value);return{tabs:s.map(Be),active:l?l.to:null}}async function I(l){d=!0,s.splice(0,s.length),i.value=null,p.value=void 0;const m=l?.tabs??[];for(const k of m)try{const C=x(e,k.to),P=Ke(k),K=_(C,P,a.keepAlive);Q(s,K,"last",null)}catch{}d=!1;const b=l?.active??m[m.length-1]?.to??a.defaultRoute;if(b)try{await e.replace(b)}catch{}}return n.watch(()=>e.currentRoute.value,l=>{if(d)return;const m=E(l);i.value=m.id,p.value=m,ce(s,a.maxAlive,i.value)},{immediate:!0}),a.initialTabs.length&&a.initialTabs.forEach(l=>{const m=x(e,l.to),b=_(m,l,a.keepAlive);Q(s,b,"last",null)}),{options:a,tabs:s,activeId:i,current:p,includeKeys:c,refreshingKey:v,openTab:A,closeTab:N,removeTab:O,refreshTab:M,refreshAll:oe,reset:ae,reload:T,getRouteKey:W,matchRoute:g,snapshot:D,hydrate:I}}function ue(e){return e?typeof e=="string"?{name:e}:e:{}}const V=Symbol("RouterTabsContext"),Y="router-tabs:snapshot";function Z(e={}){const{optional:o=!1}=e,a=n.inject(V,null);if(a)return a;const s=n.inject("$tabs",null);if(s)return s;const p=n.getCurrentInstance()?.appContext.config.globalProperties.$tabs;if(p)return p;if(!o)throw new Error("[RouterTabs] useRouterTabs must be used within <router-tab>.");return null}const Ie=864e5;function Se(e){if(typeof document>"u")return null;const o=`${encodeURIComponent(e)}=`,a=document.cookie?document.cookie.split("; "):[];for(const s of a)if(s.startsWith(o))return decodeURIComponent(s.slice(o.length));return null}function de(e,o,a){if(typeof document>"u")return;const{expiresInDays:s=7,path:i="/",domain:p,secure:v,sameSite:c="lax"}=a,d=[`${encodeURIComponent(e)}=${encodeURIComponent(o)}`];if(s!==1/0){const g=new Date(Date.now()+s*Ie).toUTCString();d.push(`Expires=${g}`)}i&&d.push(`Path=${i}`),p&&d.push(`Domain=${p}`),v&&d.push("Secure"),c&&d.push(`SameSite=${c.charAt(0).toUpperCase()}${c.slice(1)}`),document.cookie=d.join("; ")}function fe(e,o){if(typeof document>"u")return;const{path:a="/",domain:s}=o,i=[`${encodeURIComponent(e)}=`];i.push("Expires=Thu, 01 Jan 1970 00:00:01 GMT"),a&&i.push(`Path=${a}`),s&&i.push(`Domain=${s}`),document.cookie=i.join("; ")}const De=e=>JSON.stringify(e??null),xe=e=>{if(!e)return null;try{return JSON.parse(e)}catch{return null}};function ee(e={}){const{cookieKey:o=Y,serialize:a=De,deserialize:s=xe}=e,i=Z({optional:!0}),p=n.ref(!0),v=c=>{n.onMounted(async()=>{const d=s(Se(o));if(d&&d.tabs?.length)try{if(p.value=!0,await c.hydrate(d),d.active){await n.nextTick();const E=c.tabs.find(A=>A.to===d.active);E&&(c.activeId.value=E.id,c.current.value=E)}}finally{p.value=!1}else if(Object.prototype.hasOwnProperty.call(e,"fallbackRoute"))try{p.value=!0;const E=e.fallbackRoute??c.options.defaultRoute;await c.reset(E)}finally{p.value=!1}else p.value=!1;const g=c.snapshot();g.tabs.length?de(o,a(g),e):fe(o,e),p.value=!1}),n.watch(()=>({tabs:c.tabs.map(d=>({to:d.to,title:d.title,tips:d.tips,icon:d.icon,tabClass:d.tabClass,closable:d.closable,renderKey:d.renderKey})),active:c.activeId.value}),()=>{if(p.value)return;const d=c.snapshot();d.tabs.length?de(o,a(d),e):fe(o,e)},{deep:!0})};i?v(i):n.onMounted(()=>{const c=Z({optional:!0});c&&v(c)})}const $e=n.defineComponent({name:"RouterTab",components:{RouterView:Re.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:Y},persistence:{type:Object,default:null},sortable:{type:Boolean,default:!0}},emits:["tab-sort","tab-sorted"],setup(e,{emit:o}){const a=n.getCurrentInstance();if(!a)throw new Error("[RouterTab] component must be used within a Vue application context.");const s=a.appContext.app.config.globalProperties.$router;if(!s)throw new Error("[RouterTab] Vue Router is required. Make sure to call app.use(router) before RouterTab.");const i=Ae(s,{initialTabs:e.tabs,keepAlive:e.keepAlive,maxAlive:e.maxAlive,keepLastTab:e.keepLastTab,appendPosition:e.append,defaultRoute:e.defaultPage});n.provide(V,i),a.appContext.config.globalProperties.$tabs=i;const p=n.computed(()=>!!a?.slots?.default),v=n.computed(()=>!!a?.slots?.start),c=n.computed(()=>!!a?.slots?.end),d=n.ref(0),g=n.computed(()=>{d.value;const t={};return i.tabs.forEach(r=>{const f=typeof r.title=="string"?r.title:String(r.title||re(r));t[r.id]=f}),t});function E(){d.value++}const A=new Map,S=new Map;function N(t,r){if(!r||A.has(t))return;A.set(t,r);const f=i.tabs.find(h=>i.getRouteKey(h.to)===t);if(!f)return;const y=[];if(r.routeTabTitle!==void 0){const h=n.watch(()=>{const u=r.routeTabTitle;return u&&typeof u=="object"&&"value"in u?u.value:u},u=>{if(u!=null){const L=String(u);f.title=L,E()}},{immediate:!0});y.push(h)}if(r.routeTabIcon!==void 0){const h=n.watch(()=>{const u=r.routeTabIcon;return u&&typeof u=="object"&&"value"in u?u.value:u},u=>{u!=null&&(f.icon=String(u),E())},{immediate:!0});y.push(h)}if(r.routeTabClosable!==void 0){const h=n.watch(()=>{const u=r.routeTabClosable;return u&&typeof u=="object"&&"value"in u?u.value:u},u=>{u!=null&&(f.closable=!!u,E())},{immediate:!0});y.push(h)}if(r.routeTabMeta!==void 0){const h=n.watch(()=>{const u=r.routeTabMeta;return u&&typeof u=="object"&&"value"in u?u.value:u},u=>{u&&typeof u=="object"&&(Object.assign(f,u),E())},{immediate:!0,deep:!0});y.push(h)}S.set(t,y)}function O(t){const r=S.get(t);r&&(r.forEach(f=>f()),S.delete(t)),A.delete(t)}function M(t,r){t?t.routeTabTitle!==void 0||t.routeTabIcon!==void 0||t.routeTabClosable!==void 0?N(r,t):t.$&&(t.$.routeTabTitle!==void 0||t.$.routeTabIcon!==void 0||t.$.routeTabClosable!==void 0)&&N(r,t.$):t===null&&O(r)}if(e.cookieKey!==null||e.persistence){const t={...e.persistence??{}};e.cookieKey!==null?t.cookieKey=e.cookieKey||Y:t.cookieKey||(t.cookieKey=Y),ee(t)}const oe=n.computed(()=>ue(e.tabTransition)),ae=n.computed(()=>ue(e.pageTransition)),T=n.reactive({visible:!1,target:null,position:{x:0,y:0}}),W=n.ref(null),D=n.ref([]),I=n.ref(-1),l=n.reactive({dragging:!1,dragIndex:-1,dropIndex:-1,dragTab:null}),m=["refresh","refreshAll","close","closeLefts","closeRights","closeOthers"];function b(t){return i.tabs.findIndex(r=>r.id===t)}function k(t){const r=b(t.id);return r>0?i.tabs.slice(0,r):[]}function C(t){const r=b(t.id);return r>-1?i.tabs.slice(r+1):[]}function P(t){return i.tabs.filter(r=>r.id!==t.id)}async function K(t,r){const f=t.filter(y=>y.closable!==!1);if(f.length){for(const y of f)i.activeId.value===y.id?await i.closeTab(y.id,{redirect:r.to,force:!0}):await i.removeTab(y.id,{force:!0});i.activeId.value!==r.id&&await i.openTab(r.to,!0,!1)}}const tt={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})=>ie(t)},closeLefts:{label:"Close to the Left",handler:async({target:t})=>{await K(k(t),t)},enable:({target:t})=>k(t).some(r=>r.closable!==!1)},closeRights:{label:"Close to the Right",handler:async({target:t})=>{await K(C(t),t)},enable:({target:t})=>C(t).some(r=>r.closable!==!1)},closeOthers:{label:"Close Others",handler:async({target:t})=>{await K(P(t),t)},enable:({target:t})=>P(t).some(r=>r.closable!==!1)}};function B(){T.visible=!1,T.target=null,I.value=-1,D.value=[]}function nt(t,r){e.contextmenu&&(T.target=t,T.position.x=r.clientX,T.position.y=r.clientY,n.nextTick(()=>{T.visible=!0,document.addEventListener("click",B,{once:!0}),n.nextTick(()=>{at()})}))}function ot(t,r){const f=typeof t=="string"?{id:t}:t,y=tt[f.id],h=f.label??y?.label??String(f.id),u=f.visible??y?.visible??!0;if(!(typeof u=="function"?u(r):u!==!1))return null;const le=f.enable??y?.enable??!0,vt=typeof le=="function"?le(r):le!==!1,Ce=f.handler??y?.handler;if(!Ce)return null;const wt=async()=>{await Promise.resolve(Ce(r))};return{id:String(f.id),label:h,disabled:!vt,action:wt}}const U=n.computed(()=>{if(!T.visible||!T.target||e.contextmenu===!1)return[];const t=Array.isArray(e.contextmenu)?e.contextmenu:m,r={target:T.target,controller:i};return t.map(f=>ot(f,r)).filter(f=>!!f)});function at(){const t=W.value;if(!t)return;const r=8,{innerWidth:f,innerHeight:y}=window,h=t.getBoundingClientRect();let u=T.position.x,L=T.position.y;h.right>f-r&&(u=Math.max(r,f-h.width-r)),h.bottom>y-r&&(L=Math.max(r,y-h.height-r)),(u!==T.position.x||L!==T.position.y)&&(T.position.x=u,T.position.y=L)}function rt(t,r){D.value[r]=t??null}function it(t){if(t<0)return;D.value[t]?.focus({preventScroll:!0})}function q(t,r,f=U.value){if(!f.length)return-1;const y=f.length;let h=t;for(let u=0;u<y;u++)if(h=(h+r+y)%y,!f[h].disabled)return h;return-1}function z(t){I.value=t,!(t<0)&&n.nextTick(()=>it(t))}function Te(t){const r=q(I.value,t);r!==-1&&z(r)}function lt(t){if(!T.visible)return;const r=t.key,f=U.value;if(!f.length)return;if(r==="Tab"){B();return}if(["ArrowDown","ArrowUp","ArrowRight","ArrowLeft","Home","End","Enter"," ","Spacebar","Escape"].includes(r))switch(t.preventDefault(),r){case"ArrowDown":case"ArrowRight":Te(1);break;case"ArrowUp":case"ArrowLeft":Te(-1);break;case"Home":z(q(-1,1));break;case"End":z(q(f.length,-1));break;case"Enter":case" ":case"Spacebar":{const h=I.value;if(h>-1){const u=f[h];u.disabled||ke(u)}break}case"Escape":B();break}}async function ke(t){t.disabled||(B(),await t.action())}function re(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 st(t){return g.value[t.id]||re(t)}function ve(t){const r=i.getRouteKey(t),f=i.tabs.find(h=>h.id===r);if(!f)return console.warn("[RouterTab] Tab not found for route:",r),`${r}::0`;const y=f.renderKey??0;return`${r}::${y}`}function ct(t){return`${ve(t)}::refresh`}function ie(t){return!(t.closable===!1||i.options.keepLastTab&&i.tabs.length<=1)}async function ut(t){await i.closeTab(t.id)}function dt(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)}function ft(t){return["router-tab__item",{"is-active":i.activeId.value===t.id,"is-closable":ie(t),"is-dragging":l.dragging&&l.dragTab?.id===t.id,"is-drag-over":l.dropIndex===b(t.id)},t.tabClass]}function bt(t){return i.refreshingKey.value===i.getRouteKey(t)}function mt(t){const r=i.getRouteKey(t);return i.tabs.some(f=>f.id===r)}function pt(t,r,f){e.sortable&&(l.dragging=!0,l.dragIndex=r,l.dragTab=t,f.dataTransfer&&(f.dataTransfer.effectAllowed="move",f.dataTransfer.setData("text/plain",t.id)),o("tab-sort",{tab:t,index:r}))}function gt(t,r){!e.sortable||!l.dragging||(r.preventDefault(),r.dataTransfer&&(r.dataTransfer.dropEffect="move"))}function yt(t){!e.sortable||!l.dragging||(l.dropIndex=t)}function ht(){!e.sortable||l.dragging}function Tt(t,r){if(!(!e.sortable||!l.dragging)){if(r.preventDefault(),l.dragIndex!==-1&&l.dragIndex!==t){const f=i.tabs.splice(l.dragIndex,1)[0];i.tabs.splice(t,0,f),o("tab-sorted",{tab:f,fromIndex:l.dragIndex,toIndex:t})}we()}}function we(){l.dragging=!1,l.dragIndex=-1,l.dropIndex=-1,l.dragTab=null}n.onMounted(()=>{document.addEventListener("keydown",B)}),n.onBeforeUnmount(()=>{document.removeEventListener("keydown",B),a.appContext.config.globalProperties.$tabs=null,S.forEach(t=>{t.forEach(r=>r())}),S.clear(),A.clear()}),n.watch(()=>e.keepAlive,t=>{i.options.keepAlive=t}),n.watch(()=>i.activeId.value,()=>B()),n.watch(()=>e.contextmenu,t=>{t||B()}),n.watch(()=>U.value.length,t=>{T.visible&&t===0&&B()},{flush:"post"}),n.watch(U,t=>{if(!T.visible)return;D.value=new Array(t.length).fill(null);const r=q(-1,1,t);z(r)},{flush:"post"}),n.watch(()=>T.visible,t=>{t||(I.value=-1,D.value=[])});const kt=i.includeKeys;return{controller:i,tabs:i.tabs,includeKeys:kt,tabTransitionProps:oe,pageTransitionProps:ae,buildTabClass:ft,activate:dt,close:ut,context:T,menuItems:U,handleMenuAction:ke,showContextMenu:nt,hideContextMenu:B,getTabTitle:re,isClosable:ie,isRefreshing:bt,isTabReady:mt,hasCustomSlot:p,hasStartSlot:v,hasEndSlot:c,onDragStart:pt,onDragOver:gt,onDragEnter:yt,onDragLeave:ht,onDrop:Tt,onDragEnd:we,setupComponentWatching:N,cleanupComponentWatching:O,handleComponentRef:M,getReactiveTabTitle:st,getComponentCacheKey:ve,getRefreshComponentKey:ct,triggerTabUpdate:E,menuRef:W,highlightedIndex:I,setMenuItemRef:rt,onMenuKeydown:lt,highlightMenuIndex:z}}}),Me=(e,o)=>{const a=e.__vccOpts||e;for(const[s,i]of o)a[s]=i;return a},Le={class:"router-tab"},Ve={class:"router-tab__header"},je={class:"router-tab__scroll"},Ne=["data-title","draggable","onClick","onAuxclick","onContextmenu","onDragstart","onDragover","onDragenter","onDrop"],Oe=["title"],Ue=["onClick"],ze={class:"router-tab__container"},_e=["aria-disabled","disabled","tabindex","onMouseenter","onClick"];function Ye(e,o,a,s,i,p){const v=n.resolveComponent("RouterView");return n.openBlock(),n.createElementBlock("div",Le,[n.createElementVNode("header",Ve,[n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-start",{"has-content":e.hasStartSlot}])},[n.renderSlot(e.$slots,"start")],2),n.createElementVNode("div",je,[n.createVNode(n.TransitionGroup,n.mergeProps({tag:"ul",class:"router-tab__nav"},e.tabTransitionProps),{default:n.withCtx(()=>[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(e.tabs,(c,d)=>(n.openBlock(),n.createElementBlock("li",{key:c.id,class:n.normalizeClass(e.buildTabClass(c)),"data-title":e.getTabTitle(c),draggable:e.sortable,onClick:g=>e.activate(c),onAuxclick:n.withModifiers(g=>e.close(c),["middle","prevent"]),onContextmenu:n.withModifiers(g=>e.showContextMenu(c,g),["prevent"]),onDragstart:g=>e.onDragStart(c,d,g),onDragover:g=>e.onDragOver(d,g),onDragenter:g=>e.onDragEnter(d),onDragleave:o[0]||(o[0]=(...g)=>e.onDragLeave&&e.onDragLeave(...g)),onDrop:g=>e.onDrop(d,g),onDragend:o[1]||(o[1]=(...g)=>e.onDragEnd&&e.onDragEnd(...g))},[c.icon?(n.openBlock(),n.createElementBlock("i",{key:0,class:n.normalizeClass(["router-tab__item-icon",c.icon])},null,2)):n.createCommentVNode("",!0),n.createElementVNode("span",{class:"router-tab__item-title",title:e.getReactiveTabTitle(c)},n.toDisplayString(e.getReactiveTabTitle(c)),9,Oe),e.isClosable(c)?(n.openBlock(),n.createElementBlock("a",{key:1,class:"router-tab__item-close",onClick:n.withModifiers(g=>e.close(c),["stop"])},null,8,Ue)):n.createCommentVNode("",!0)],42,Ne))),128))]),_:1},16)]),n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-end",{"has-content":e.hasEndSlot}])},[n.renderSlot(e.$slots,"end")],2)]),n.createElementVNode("div",ze,[n.createVNode(v,null,{default:n.withCtx(c=>[e.hasCustomSlot?n.renderSlot(e.$slots,"default",n.normalizeProps(n.mergeProps({key:0},{...c,controller:e.controller,pageRef:d=>e.handleComponentRef(d,e.controller.getRouteKey(c.route))}))):(n.openBlock(),n.createBlock(n.Transition,n.mergeProps({key:1},e.pageTransitionProps,{appear:""}),{default:n.withCtx(()=>[e.isRefreshing(c.route)?(n.openBlock(),n.createBlock(n.resolveDynamicComponent(c.Component),{key:e.getRefreshComponentKey(c.route),ref:d=>e.handleComponentRef(d,e.controller.getRouteKey(c.route)),class:"router-tab-page"})):e.controller.options.keepAlive?(n.openBlock(),n.createBlock(n.KeepAlive,{key:1,include:e.includeKeys,max:e.controller.options.maxAlive||void 0},[e.isTabReady(c.route)?(n.openBlock(),n.createBlock(n.resolveDynamicComponent(c.Component),{key:e.getComponentCacheKey(c.route),ref:d=>e.handleComponentRef(d,e.controller.getRouteKey(c.route)),class:"router-tab-page"})):n.createCommentVNode("",!0)],1032,["include","max"])):e.isTabReady(c.route)?(n.openBlock(),n.createBlock(n.resolveDynamicComponent(c.Component),{key:e.getComponentCacheKey(c.route),ref:d=>e.handleComponentRef(d,e.controller.getRouteKey(c.route)),class:"router-tab-page"})):n.createCommentVNode("",!0)]),_:2},1040))]),_:3})]),n.withDirectives(n.createElementVNode("div",{ref:"menuRef",class:"router-tab__contextmenu",role:"menu",onKeydown:o[2]||(o[2]=(...c)=>e.onMenuKeydown&&e.onMenuKeydown(...c)),style:n.normalizeStyle({left:e.context.position.x+"px",top:e.context.position.y+"px"})},[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(e.menuItems,(c,d)=>(n.openBlock(),n.createElementBlock("a",{key:c.id,role:"menuitem",class:n.normalizeClass(["router-tab__contextmenu-item",{"is-focused":d===e.highlightedIndex}]),"aria-disabled":c.disabled,disabled:c.disabled,tabindex:c.disabled?-1:d===e.highlightedIndex?0:-1,ref_for:!0,ref:g=>e.setMenuItemRef(g,d),onMouseenter:g=>!c.disabled&&e.highlightMenuIndex(d),onClick:g=>e.handleMenuAction(c)},n.toDisplayString(c.label),43,_e))),128))],36),[[n.vShow,e.context.visible&&e.context.target]])])}const te=Me($e,[["render",Ye]]),Fe={class:"router-tabs","aria-hidden":"true"},F=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 ee(e),(a,s)=>(n.openBlock(),n.createElementBlock("span",Fe))}}),be="tab-theme-style",me="tab-theme-primary-color",He="system",pe="(prefers-color-scheme: dark)";let j=null;const R={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"},Je={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 We(e){if(typeof window>"u")return null;const o=window.localStorage.getItem(e);if(!o)return null;try{const a=JSON.parse(o);return a&&typeof a=="object"?a:null}catch{return null}}function ne(e){typeof document>"u"||(document.documentElement.style.setProperty("--router-tab-primary",e.primary??R.primary),document.documentElement.style.setProperty("--router-tab-header-bg",e.headerBackground??R.headerBackground),document.documentElement.style.setProperty("--router-tab-background",e.background??R.background),document.documentElement.style.setProperty("--router-tab-active-background",e.activeBackground??R.activeBackground),document.documentElement.style.setProperty("--router-tab-text",e.text??R.text),document.documentElement.style.setProperty("--router-tab-active-text",e.activeText??R.activeText),document.documentElement.style.setProperty("--router-tab-border",e.border??R.border),document.documentElement.style.setProperty("--router-tab-active-border",e.activeBorder??R.activeBorder),document.documentElement.style.setProperty("--router-tab-button-color",e.buttonColor??R.buttonColor),document.documentElement.style.setProperty("--router-tab-active-button-color",e.activeButtonColor??R.activeButtonColor),document.documentElement.style.setProperty("--router-tab-button-background",e.buttonBackground??R.buttonBackground),document.documentElement.style.setProperty("--router-tab-active-button-background",e.activeButtonBackground??R.activeButtonBackground),document.documentElement.style.setProperty("--router-tab-icon-color",e.iconColor??R.iconColor))}function ge(e){if(typeof document>"u")return;const o=document.documentElement,a=window.matchMedia(pe),s=()=>{o.dataset.theme=a.matches?"dark":"light"};j&&(a.removeEventListener("change",j),j=null),e==="system"?(s(),j=()=>s(),a.addEventListener("change",j)):o.dataset.theme=e}function ye(e={}){if(typeof window>"u")return;const{styleKey:o=be,primaryKey:a=me,defaultStyle:s=He,defaultPrimary:i}=e,p=window.localStorage.getItem(o)??s;ge(p);const c=p==="dark"||p==="system"&&window.matchMedia(pe).matches?{...Je}:{...R};i&&(c.primary=i);const d=We(a);ne(d?{...c,...d}:c)}function qe(e,o){if(typeof window>"u")return;const a=o?.styleKey??be;window.localStorage.setItem(a,e),ge(e)}function Ge(e,o){if(typeof window>"u")return;const a=o?.primaryKey??me;window.localStorage.setItem(a,JSON.stringify(e)),ne(e)}function H(e,o){if(n.isRef(e)){const s=!n.isReadonly(e);return{value:e,update:s?i=>{e.value=i}:()=>{}}}if(typeof e=="function"){const s=e;return{value:n.computed(s),update:()=>{}}}const a=n.ref(e===void 0?o:e);return{value:a,update:s=>{a.value=s}}}function J(e={}){const o=H(e.title,"Untitled"),a=H(e.icon,""),s=H(e.closable,!0),i=H(e.meta,{});return{routeTabTitle:o.value,routeTabIcon:a.value,routeTabClosable:s.value,routeTabMeta:i.value,updateTitle:o.update,updateIcon:a.update,updateClosable:s.update,updateMeta:i.update}}function Xe(e,o="Page"){return J({title:n.computed(()=>e.value?"Loading...":o),icon:n.computed(()=>e.value?"mdi-loading mdi-spin":"mdi-page"),closable:n.computed(()=>!e.value)})}function Qe(e,o="Page",a="mdi-page"){return J({title:n.computed(()=>e.value>0?`${o} (${e.value})`:o),icon:n.computed(()=>e.value>0?"mdi-bell-badge":a)})}function Ze(e,o="Page"){const a={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 J({title:n.computed(()=>o+a[e.value].suffix),icon:n.computed(()=>a[e.value].icon),closable:n.computed(()=>e.value!=="loading")})}let he=!1;const et={install(e,o){if(he)return;he=!0;const{initTheme:a=!0,themeOptions:s,componentName:i=te.name||"RouterTab",tabsComponentName:p=F.name||"RouterTabs"}=o??{};a&&ye(s??{}),e.component(i,te),e.component(p,F),p.toLowerCase()!=="router-tabs"&&e.component("router-tabs",F),Object.defineProperty(e.config.globalProperties,"$tabs",{configurable:!0,enumerable:!1,get(){return e._context.provides[V]},set(v){v&&e.provide(V,v)}})}};w.RouterTab=te,w.RouterTabs=F,w.default=et,w.initRouterTabsTheme=ye,w.routerTabsKey=V,w.setRouterTabsPrimary=Ge,w.setRouterTabsTheme=qe,w.useLoadingTab=Xe,w.useNotificationTab=Qe,w.useReactiveTab=J,w.useRouterTabs=Z,w.useRouterTabsPersistence=ee,w.useStatusTab=Ze,Object.defineProperties(w,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
1
+ (function(w,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):(w=typeof globalThis<"u"?globalThis:w||self,n(w["vue3-router-tab"]={},w.Vue,w.VueRouter))})(this,(function(w,n,Ce){"use strict";function Ee(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,o){const r=e.resolve(o);if(!r||!r.matched.length)throw new Error(`[RouterTabs] Unable to resolve route: ${String(o)}`);return r}const Pe={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 o=e.meta?.key;if(typeof o=="function"){const r=o(e);if(typeof r=="string"&&r.length)return r}else if(typeof o=="string"&&o.length){const r=Pe[o.toLowerCase()];return r?r(e):o}return e.fullPath}function G(e,o){const r=e.meta?.keepAlive;return typeof r=="boolean"?r:o}function X(e,o){const r=e.meta?.reuse;return typeof r=="boolean"?r:o}function se(e){const o=e.meta??{},r={};return"title"in o&&(r.title=o.title),"tips"in o&&(r.tips=o.tips),"icon"in o&&(r.icon=o.icon),"closable"in o&&(r.closable=o.closable),"tabClass"in o&&(r.tabClass=o.tabClass),"target"in o&&(r.target=o.target),"href"in o&&(r.href=o.href),r}function _(e,o,r){const s=se(e);return{id:M(e),to:e.fullPath,fullPath:e.fullPath,matched:e,alive:G(e,r),reusable:X(e,!1),closable:s.closable??!0,renderKey:typeof o.renderKey=="number"?o.renderKey:0,...s,...o}}function Q(e,o,r,s){if(!e.find(g=>g.id===o.id)){if(r==="next"&&s){const g=e.findIndex(v=>v.id===s);if(g!==-1){e.splice(g+1,0,o);return}}e.push(o)}}function ce(e,o,r){if(!o||o<=0)return;const s=e.filter(i=>i.alive);for(;s.length>o;){const i=s.shift();if(!i||i.id===r)continue;const g=e.findIndex(v=>v.id===i.id);g>-1&&(e[g].alive=!1)}}function Be(e){return{to:e.to,title:e.title,tips:e.tips,icon:e.icon,tabClass:e.tabClass,closable:e.closable,renderKey:e.renderKey}}function Ae(e){const o={};return"title"in e&&(o.title=e.title),"tips"in e&&(o.tips=e.tips),"icon"in e&&(o.icon=e.icon),"tabClass"in e&&(o.tabClass=e.tabClass),"closable"in e&&(o.closable=e.closable),"renderKey"in e&&typeof e.renderKey=="number"&&(o.renderKey=e.renderKey),o}function Ie(e,o={}){const r=Ee(o),s=n.reactive([]),i=n.ref(null),g=n.shallowRef(),v=n.ref(null),c=n.computed(()=>s.filter(l=>l.alive).map(l=>`${l.id}::${l.renderKey??0}`));let f=!1;function y(l){const m=typeof l.matched=="object"?l:x(e,l);return{key:M(m),fullPath:m.fullPath,alive:G(m,r.keepAlive),reusable:X(m,!1),matched:m}}function E(l){const m=M(l);let b=s.find(k=>k.id===m);return b?(b.fullPath=l.fullPath,b.to=l.fullPath,b.matched=l,b.alive=G(l,r.keepAlive),b.reusable=X(l,b.reusable),typeof b.renderKey!="number"&&(b.renderKey=0),Object.assign(b,se(l)),b):(b=_(l,{},r.keepAlive),Q(s,b,r.appendPosition,i.value),ce(s,r.maxAlive,i.value),b)}async function A(l,m=!1,b=!0){const k=x(e,l),R=M(k),P=i.value===R;b==="sameTab"&&(b=P),b&&await L(R,!0),await e[m?"replace":"push"](k),P&&await T()}function S(l){const m=s.findIndex($=>$.id===l);if(m===-1)return r.defaultRoute;const b=s[m+1],k=s[m-1],R=s.find($=>$.id!==l),P=b||k||R;return P?P.to:r.defaultRoute}async function O(l=i.value,m={}){if(!l)return;if(!m.force&&r.keepLastTab&&s.length===1)throw new Error("[RouterTabs] Unable to close the final tab when keepLastTab is true.");const k=i.value===l&&m.redirect!==null,R=k?m.redirect??S(l):null;await I(l,{force:m.force}),m.redirect!==null&&k&&R&&await e.replace(R)}async function I(l,m={}){const b=s.findIndex(k=>k.id===l);b!==-1&&(s.splice(b,1),v.value===l&&(v.value=null),i.value===l&&(i.value=null,g.value=void 0))}async function L(l=i.value??void 0,m=!1){if(!l)return;const b=s.find(R=>R.id===l);if(!b)return;const k=r.keepAlive&&b.alive;k&&(b.alive=!1,await n.nextTick()),b.renderKey=(b.renderKey??0)+1,k&&(b.alive=!0),v.value=l,await n.nextTick(),m||await n.nextTick(),v.value=null}async function oe(l=!1){for(const m of s)await L(m.id,l)}async function re(l=r.defaultRoute){s.splice(0,s.length),i.value=null,g.value=void 0;for(const m of r.initialTabs){const b=x(e,m.to),k=_(b,m,r.keepAlive);s.push(k)}await e.replace(l)}async function T(){const l=i.value;l&&await L(l,!0)}function J(l){return typeof l.matched=="object"?M(l):M(x(e,l))}function D(){const l=s.find(m=>m.id===i.value);return{tabs:s.map(Be),active:l?l.to:null}}async function K(l){f=!0,s.splice(0,s.length),i.value=null,g.value=void 0;const m=l?.tabs??[];for(const k of m)try{const R=x(e,k.to),P=Ae(k),$=_(R,P,r.keepAlive);Q(s,$,"last",null)}catch{}f=!1;const b=l?.active??m[m.length-1]?.to??r.defaultRoute;if(b)try{await e.replace(b)}catch{}}return n.watch(()=>e.currentRoute.value,l=>{if(f)return;const m=E(l);i.value=m.id,g.value=m,ce(s,r.maxAlive,i.value)},{immediate:!0}),r.initialTabs.length&&r.initialTabs.forEach(l=>{const m=x(e,l.to),b=_(m,l,r.keepAlive);Q(s,b,"last",null)}),{options:r,tabs:s,activeId:i,current:g,includeKeys:c,refreshingKey:v,openTab:A,closeTab:O,removeTab:I,refreshTab:L,refreshAll:oe,reset:re,reload:T,getRouteKey:J,matchRoute:y,snapshot:D,hydrate:K}}function ue(e){return e?typeof e=="string"?{name:e}:e:{}}const j=Symbol("RouterTabsContext"),Y="router-tabs:snapshot";function Z(e={}){const{optional:o=!1}=e,r=n.inject(j,null);if(r)return r;const s=n.inject("$tabs",null);if(s)return s;const g=n.getCurrentInstance()?.appContext.config.globalProperties.$tabs;if(g)return g;if(!o)throw new Error("[RouterTabs] useRouterTabs must be used within <router-tab>.");return null}const $e=864e5;function Ke(e){if(typeof document>"u")return null;const o=`${encodeURIComponent(e)}=`,r=document.cookie?document.cookie.split("; "):[];for(const s of r)if(s.startsWith(o))return decodeURIComponent(s.slice(o.length));return null}function de(e,o,r){if(typeof document>"u")return;const{expiresInDays:s=7,path:i="/",domain:g,secure:v,sameSite:c="lax"}=r,f=[`${encodeURIComponent(e)}=${encodeURIComponent(o)}`];if(s!==1/0){const y=new Date(Date.now()+s*$e).toUTCString();f.push(`Expires=${y}`)}i&&f.push(`Path=${i}`),g&&f.push(`Domain=${g}`),v&&f.push("Secure"),c&&f.push(`SameSite=${c.charAt(0).toUpperCase()}${c.slice(1)}`),document.cookie=f.join("; ")}function fe(e,o){if(typeof document>"u")return;const{path:r="/",domain:s}=o,i=[`${encodeURIComponent(e)}=`];i.push("Expires=Thu, 01 Jan 1970 00:00:01 GMT"),r&&i.push(`Path=${r}`),s&&i.push(`Domain=${s}`),document.cookie=i.join("; ")}const Se=e=>JSON.stringify(e??null),De=e=>{if(!e)return null;try{return JSON.parse(e)}catch{return null}};function ee(e={}){const{cookieKey:o=Y,serialize:r=Se,deserialize:s=De}=e,i=Z({optional:!0}),g=n.ref(!0),v=c=>{n.onMounted(async()=>{const f=s(Ke(o));if(f&&f.tabs?.length)try{if(g.value=!0,await c.hydrate(f),f.active){await n.nextTick();const E=c.tabs.find(A=>A.to===f.active);E&&(c.activeId.value=E.id,c.current.value=E)}}finally{g.value=!1}else if(Object.prototype.hasOwnProperty.call(e,"fallbackRoute"))try{g.value=!0;const E=e.fallbackRoute??c.options.defaultRoute;await c.reset(E)}finally{g.value=!1}else g.value=!1;const y=c.snapshot();y.tabs.length?de(o,r(y),e):fe(o,e),g.value=!1}),n.watch(()=>({tabs:c.tabs.map(f=>({to:f.to,title:f.title,tips:f.tips,icon:f.icon,tabClass:f.tabClass,closable:f.closable,renderKey:f.renderKey})),active:c.activeId.value}),()=>{if(g.value)return;const f=c.snapshot();f.tabs.length?de(o,r(f),e):fe(o,e)},{deep:!0})};i?v(i):n.onMounted(()=>{const c=Z({optional:!0});c&&v(c)})}const xe=n.defineComponent({name:"RouterTab",components:{RouterView:Ce.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:Y},persistence:{type:Object,default:null},sortable:{type:Boolean,default:!0}},emits:["tab-sort","tab-sorted"],setup(e,{emit:o}){const r=n.getCurrentInstance();if(!r)throw new Error("[RouterTab] component must be used within a Vue application context.");const s=r.appContext.app.config.globalProperties.$router;if(!s)throw new Error("[RouterTab] Vue Router is required. Make sure to call app.use(router) before RouterTab.");const i=Ie(s,{initialTabs:e.tabs,keepAlive:e.keepAlive,maxAlive:e.maxAlive,keepLastTab:e.keepLastTab,appendPosition:e.append,defaultRoute:e.defaultPage});n.provide(j,i),r.appContext.config.globalProperties.$tabs=i;const g=n.computed(()=>!!r?.slots?.default),v=n.computed(()=>!!r?.slots?.start),c=n.computed(()=>!!r?.slots?.end),f=n.ref(0),y=n.computed(()=>{f.value;const t={};return i.tabs.forEach(a=>{const u=typeof a.title=="string"?a.title:String(a.title||ae(a));t[a.id]=u}),t});function E(){f.value++}const A=new Map,S=new Map;function O(t,a){if(!(!a||A.has(t)))try{A.set(t,a);const u=i.tabs.find(p=>i.getRouteKey(p.to)===t);if(!u){console.warn(`[RouterTab] Cannot setup watching: tab not found for ${t}`);return}const h=[];if(a.routeTabTitle!==void 0)try{const p=n.watch(()=>{const d=a.routeTabTitle;return d&&typeof d=="object"&&"value"in d?d.value:d},d=>{if(d!=null){const V=String(d);u.title=V,E()}},{immediate:!0});h.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabTitle for ${t}:`,p)}if(a.routeTabIcon!==void 0)try{const p=n.watch(()=>{const d=a.routeTabIcon;return d&&typeof d=="object"&&"value"in d?d.value:d},d=>{d!=null&&(u.icon=String(d),E())},{immediate:!0});h.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabIcon for ${t}:`,p)}if(a.routeTabClosable!==void 0)try{const p=n.watch(()=>{const d=a.routeTabClosable;return d&&typeof d=="object"&&"value"in d?d.value:d},d=>{d!=null&&(u.closable=!!d,E())},{immediate:!0});h.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabClosable for ${t}:`,p)}if(a.routeTabMeta!==void 0)try{const p=n.watch(()=>{const d=a.routeTabMeta;return d&&typeof d=="object"&&"value"in d?d.value:d},d=>{d&&typeof d=="object"&&(Object.assign(u,d),E())},{immediate:!0,deep:!0});h.push(p)}catch(p){console.error(`[RouterTab] Error watching routeTabMeta for ${t}:`,p)}S.set(t,h)}catch(u){console.error(`[RouterTab] Error in setupComponentWatching for ${t}:`,u),I(t)}}function I(t){try{const a=S.get(t);a&&(a.forEach(u=>{try{u()}catch(h){console.error(`[RouterTab] Error cleaning up watcher for ${t}:`,h)}}),S.delete(t)),A.delete(t)}catch(a){console.error(`[RouterTab] Error in cleanupComponentWatching for ${t}:`,a)}}function L(t,a){try{t?t.routeTabTitle!==void 0||t.routeTabIcon!==void 0||t.routeTabClosable!==void 0?O(a,t):t.$&&(t.$.routeTabTitle!==void 0||t.$.routeTabIcon!==void 0||t.$.routeTabClosable!==void 0)&&O(a,t.$):t===null&&I(a)}catch(u){console.error(`[RouterTab] Error handling component ref for ${a}:`,u),I(a)}}if(n.onErrorCaptured((t,a,u)=>{if(console.error("[RouterTab] Error captured from component:",t,u),a&&i.activeId.value){const h=i.activeId.value;I(h);const p=i.tabs.find(d=>d.id===h);p&&p.alive&&(console.warn(`[RouterTab] Removing errored component ${h} from KeepAlive cache`),p.alive=!1,n.nextTick(()=>{p.alive=!0}))}return!1}),e.cookieKey!==null||e.persistence){const t={...e.persistence??{}};e.cookieKey!==null?t.cookieKey=e.cookieKey||Y:t.cookieKey||(t.cookieKey=Y),ee(t)}const oe=n.computed(()=>ue(e.tabTransition)),re=n.computed(()=>ue(e.pageTransition)),T=n.reactive({visible:!1,target:null,position:{x:0,y:0}}),J=n.ref(null),D=n.ref([]),K=n.ref(-1),l=n.reactive({dragging:!1,dragIndex:-1,dropIndex:-1,dragTab:null}),m=["refresh","refreshAll","close","closeLefts","closeRights","closeOthers"];function b(t){return i.tabs.findIndex(a=>a.id===t)}function k(t){const a=b(t.id);return a>0?i.tabs.slice(0,a):[]}function R(t){const a=b(t.id);return a>-1?i.tabs.slice(a+1):[]}function P(t){return i.tabs.filter(a=>a.id!==t.id)}async function $(t,a){const u=t.filter(h=>h.closable!==!1);if(u.length){for(const h of u)i.activeId.value===h.id?await i.closeTab(h.id,{redirect:a.to,force:!0}):await i.removeTab(h.id,{force:!0});i.activeId.value!==a.id&&await i.openTab(a.to,!0,!1)}}const tt={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})=>ie(t)},closeLefts:{label:"Close to the Left",handler:async({target:t})=>{await $(k(t),t)},enable:({target:t})=>k(t).some(a=>a.closable!==!1)},closeRights:{label:"Close to the Right",handler:async({target:t})=>{await $(R(t),t)},enable:({target:t})=>R(t).some(a=>a.closable!==!1)},closeOthers:{label:"Close Others",handler:async({target:t})=>{await $(P(t),t)},enable:({target:t})=>P(t).some(a=>a.closable!==!1)}};function B(){T.visible=!1,T.target=null,K.value=-1,D.value=[]}function nt(t,a){e.contextmenu&&(T.target=t,T.position.x=a.clientX,T.position.y=a.clientY,n.nextTick(()=>{T.visible=!0,document.addEventListener("click",B,{once:!0}),n.nextTick(()=>{rt()})}))}function ot(t,a){const u=typeof t=="string"?{id:t}:t,h=tt[u.id],p=u.label??h?.label??String(u.id),d=u.visible??h?.visible??!0;if(!(typeof d=="function"?d(a):d!==!1))return null;const le=u.enable??h?.enable??!0,vt=typeof le=="function"?le(a):le!==!1,Re=u.handler??h?.handler;if(!Re)return null;const wt=async()=>{await Promise.resolve(Re(a))};return{id:String(u.id),label:p,disabled:!vt,action:wt}}const U=n.computed(()=>{if(!T.visible||!T.target||e.contextmenu===!1)return[];const t=Array.isArray(e.contextmenu)?e.contextmenu:m,a={target:T.target,controller:i};return t.map(u=>ot(u,a)).filter(u=>!!u)});function rt(){const t=J.value;if(!t)return;const a=8,{innerWidth:u,innerHeight:h}=window,p=t.getBoundingClientRect();let d=T.position.x,V=T.position.y;p.right>u-a&&(d=Math.max(a,u-p.width-a)),p.bottom>h-a&&(V=Math.max(a,h-p.height-a)),(d!==T.position.x||V!==T.position.y)&&(T.position.x=d,T.position.y=V)}function at(t,a){D.value[a]=t??null}function it(t){if(t<0)return;D.value[t]?.focus({preventScroll:!0})}function q(t,a,u=U.value){if(!u.length)return-1;const h=u.length;let p=t;for(let d=0;d<h;d++)if(p=(p+a+h)%h,!u[p].disabled)return p;return-1}function z(t){K.value=t,!(t<0)&&n.nextTick(()=>it(t))}function Te(t){const a=q(K.value,t);a!==-1&&z(a)}function lt(t){if(!T.visible)return;const a=t.key,u=U.value;if(!u.length)return;if(a==="Tab"){B();return}if(["ArrowDown","ArrowUp","ArrowRight","ArrowLeft","Home","End","Enter"," ","Spacebar","Escape"].includes(a))switch(t.preventDefault(),a){case"ArrowDown":case"ArrowRight":Te(1);break;case"ArrowUp":case"ArrowLeft":Te(-1);break;case"Home":z(q(-1,1));break;case"End":z(q(u.length,-1));break;case"Enter":case" ":case"Spacebar":{const p=K.value;if(p>-1){const d=u[p];d.disabled||ke(d)}break}case"Escape":B();break}}async function ke(t){t.disabled||(B(),await t.action())}function ae(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 st(t){return y.value[t.id]||ae(t)}function ve(t){const a=i.getRouteKey(t),u=i.tabs.find(p=>p.id===a);if(!u)return console.warn("[RouterTab] Tab not found for route:",a),`${a}::0`;const h=u.renderKey??0;return`${a}::${h}`}function ct(t){return`${ve(t)}::refresh`}function ie(t){return!(t.closable===!1||i.options.keepLastTab&&i.tabs.length<=1)}async function ut(t){await i.closeTab(t.id)}function dt(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)}function ft(t){return["router-tab__item",{"is-active":i.activeId.value===t.id,"is-closable":ie(t),"is-dragging":l.dragging&&l.dragTab?.id===t.id,"is-drag-over":l.dropIndex===b(t.id)},t.tabClass]}function bt(t){return i.refreshingKey.value===i.getRouteKey(t)}function mt(t){const a=i.getRouteKey(t);return i.tabs.some(u=>u.id===a)}function pt(t,a,u){e.sortable&&(l.dragging=!0,l.dragIndex=a,l.dragTab=t,u.dataTransfer&&(u.dataTransfer.effectAllowed="move",u.dataTransfer.setData("text/plain",t.id)),o("tab-sort",{tab:t,index:a}))}function gt(t,a){!e.sortable||!l.dragging||(a.preventDefault(),a.dataTransfer&&(a.dataTransfer.dropEffect="move"))}function ht(t){!e.sortable||!l.dragging||(l.dropIndex=t)}function yt(){!e.sortable||l.dragging}function Tt(t,a){if(!(!e.sortable||!l.dragging)){if(a.preventDefault(),l.dragIndex!==-1&&l.dragIndex!==t){const u=i.tabs.splice(l.dragIndex,1)[0];i.tabs.splice(t,0,u),o("tab-sorted",{tab:u,fromIndex:l.dragIndex,toIndex:t})}we()}}function we(){l.dragging=!1,l.dragIndex=-1,l.dropIndex=-1,l.dragTab=null}n.onMounted(()=>{document.addEventListener("keydown",B)}),n.onBeforeUnmount(()=>{document.removeEventListener("keydown",B),r.appContext.config.globalProperties.$tabs=null,S.forEach(t=>{t.forEach(a=>{try{a()}catch(u){console.error("[RouterTab] Error during cleanup:",u)}})}),S.clear(),A.clear()}),n.watch(()=>e.keepAlive,t=>{i.options.keepAlive=t}),n.watch(()=>i.activeId.value,()=>B()),n.watch(()=>i.tabs.length,()=>{const t=new Set(i.tabs.map(u=>u.id));Array.from(A.keys()).forEach(u=>{t.has(u)||(console.log(`[RouterTab] Cleaning up stale component instance: ${u}`),I(u))})}),n.watch(()=>e.contextmenu,t=>{t||B()}),n.watch(()=>U.value.length,t=>{T.visible&&t===0&&B()},{flush:"post"}),n.watch(U,t=>{if(!T.visible)return;D.value=new Array(t.length).fill(null);const a=q(-1,1,t);z(a)},{flush:"post"}),n.watch(()=>T.visible,t=>{t||(K.value=-1,D.value=[])});const kt=i.includeKeys;return{controller:i,tabs:i.tabs,includeKeys:kt,tabTransitionProps:oe,pageTransitionProps:re,buildTabClass:ft,activate:dt,close:ut,context:T,menuItems:U,handleMenuAction:ke,showContextMenu:nt,hideContextMenu:B,getTabTitle:ae,isClosable:ie,isRefreshing:bt,isTabReady:mt,hasCustomSlot:g,hasStartSlot:v,hasEndSlot:c,onDragStart:pt,onDragOver:gt,onDragEnter:ht,onDragLeave:yt,onDrop:Tt,onDragEnd:we,setupComponentWatching:O,cleanupComponentWatching:I,handleComponentRef:L,getReactiveTabTitle:st,getComponentCacheKey:ve,getRefreshComponentKey:ct,triggerTabUpdate:E,menuRef:J,highlightedIndex:K,setMenuItemRef:at,onMenuKeydown:lt,highlightMenuIndex:z}}}),Me=(e,o)=>{const r=e.__vccOpts||e;for(const[s,i]of o)r[s]=i;return r},Le={class:"router-tab"},Ve={class:"router-tab__header"},je={class:"router-tab__scroll"},Ne=["data-title","draggable","onClick","onAuxclick","onContextmenu","onDragstart","onDragover","onDragenter","onDrop"],Oe=["title"],Ue=["onClick"],ze={class:"router-tab__container"},_e=["aria-disabled","disabled","tabindex","onMouseenter","onClick"];function Ye(e,o,r,s,i,g){const v=n.resolveComponent("RouterView");return n.openBlock(),n.createElementBlock("div",Le,[n.createElementVNode("header",Ve,[n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-start",{"has-content":e.hasStartSlot}])},[n.renderSlot(e.$slots,"start")],2),n.createElementVNode("div",je,[n.createVNode(n.TransitionGroup,n.mergeProps({tag:"ul",class:"router-tab__nav"},e.tabTransitionProps),{default:n.withCtx(()=>[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(e.tabs,(c,f)=>(n.openBlock(),n.createElementBlock("li",{key:c.id,class:n.normalizeClass(e.buildTabClass(c)),"data-title":e.getTabTitle(c),draggable:e.sortable,onClick:y=>e.activate(c),onAuxclick:n.withModifiers(y=>e.close(c),["middle","prevent"]),onContextmenu:n.withModifiers(y=>e.showContextMenu(c,y),["prevent"]),onDragstart:y=>e.onDragStart(c,f,y),onDragover:y=>e.onDragOver(f,y),onDragenter:y=>e.onDragEnter(f),onDragleave:o[0]||(o[0]=(...y)=>e.onDragLeave&&e.onDragLeave(...y)),onDrop:y=>e.onDrop(f,y),onDragend:o[1]||(o[1]=(...y)=>e.onDragEnd&&e.onDragEnd(...y))},[c.icon?(n.openBlock(),n.createElementBlock("i",{key:0,class:n.normalizeClass(["router-tab__item-icon",c.icon])},null,2)):n.createCommentVNode("",!0),n.createElementVNode("span",{class:"router-tab__item-title",title:e.getReactiveTabTitle(c)},n.toDisplayString(e.getReactiveTabTitle(c)),9,Oe),e.isClosable(c)?(n.openBlock(),n.createElementBlock("a",{key:1,class:"router-tab__item-close",onClick:n.withModifiers(y=>e.close(c),["stop"])},null,8,Ue)):n.createCommentVNode("",!0)],42,Ne))),128))]),_:1},16)]),n.createElementVNode("div",{class:n.normalizeClass(["router-tab__slot-end",{"has-content":e.hasEndSlot}])},[n.renderSlot(e.$slots,"end")],2)]),n.createElementVNode("div",ze,[n.createVNode(v,null,{default:n.withCtx(c=>[e.hasCustomSlot?n.renderSlot(e.$slots,"default",n.normalizeProps(n.mergeProps({key:0},{...c,controller:e.controller,pageRef:f=>e.handleComponentRef(f,e.controller.getRouteKey(c.route))}))):(n.openBlock(),n.createBlock(n.Transition,n.mergeProps({key:1},e.pageTransitionProps,{appear:""}),{default:n.withCtx(()=>[e.isRefreshing(c.route)?(n.openBlock(),n.createBlock(n.resolveDynamicComponent(c.Component),{key:e.getRefreshComponentKey(c.route),ref:f=>e.handleComponentRef(f,e.controller.getRouteKey(c.route)),class:"router-tab-page"})):e.controller.options.keepAlive?(n.openBlock(),n.createBlock(n.KeepAlive,{key:1,include:e.includeKeys,max:e.controller.options.maxAlive||void 0},[e.isTabReady(c.route)?(n.openBlock(),n.createBlock(n.resolveDynamicComponent(c.Component),{key:e.getComponentCacheKey(c.route),ref:f=>e.handleComponentRef(f,e.controller.getRouteKey(c.route)),class:"router-tab-page"})):n.createCommentVNode("",!0)],1032,["include","max"])):e.isTabReady(c.route)?(n.openBlock(),n.createBlock(n.resolveDynamicComponent(c.Component),{key:e.getComponentCacheKey(c.route),ref:f=>e.handleComponentRef(f,e.controller.getRouteKey(c.route)),class:"router-tab-page"})):n.createCommentVNode("",!0)]),_:2},1040))]),_:3})]),n.withDirectives(n.createElementVNode("div",{ref:"menuRef",class:"router-tab__contextmenu",role:"menu",onKeydown:o[2]||(o[2]=(...c)=>e.onMenuKeydown&&e.onMenuKeydown(...c)),style:n.normalizeStyle({left:e.context.position.x+"px",top:e.context.position.y+"px"})},[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(e.menuItems,(c,f)=>(n.openBlock(),n.createElementBlock("a",{key:c.id,role:"menuitem",class:n.normalizeClass(["router-tab__contextmenu-item",{"is-focused":f===e.highlightedIndex}]),"aria-disabled":c.disabled,disabled:c.disabled,tabindex:c.disabled?-1:f===e.highlightedIndex?0:-1,ref_for:!0,ref:y=>e.setMenuItemRef(y,f),onMouseenter:y=>!c.disabled&&e.highlightMenuIndex(f),onClick:y=>e.handleMenuAction(c)},n.toDisplayString(c.label),43,_e))),128))],36),[[n.vShow,e.context.visible&&e.context.target]])])}const te=Me(xe,[["render",Ye]]),Fe={class:"router-tabs","aria-hidden":"true"},F=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 ee(e),(r,s)=>(n.openBlock(),n.createElementBlock("span",Fe))}}),be="tab-theme-style",me="tab-theme-primary-color",He="system",pe="(prefers-color-scheme: dark)";let N=null;const C={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"},We={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 Je(e){if(typeof window>"u")return null;const o=window.localStorage.getItem(e);if(!o)return null;try{const r=JSON.parse(o);return r&&typeof r=="object"?r:null}catch{return null}}function ne(e){typeof document>"u"||(document.documentElement.style.setProperty("--router-tab-primary",e.primary??C.primary),document.documentElement.style.setProperty("--router-tab-header-bg",e.headerBackground??C.headerBackground),document.documentElement.style.setProperty("--router-tab-background",e.background??C.background),document.documentElement.style.setProperty("--router-tab-active-background",e.activeBackground??C.activeBackground),document.documentElement.style.setProperty("--router-tab-text",e.text??C.text),document.documentElement.style.setProperty("--router-tab-active-text",e.activeText??C.activeText),document.documentElement.style.setProperty("--router-tab-border",e.border??C.border),document.documentElement.style.setProperty("--router-tab-active-border",e.activeBorder??C.activeBorder),document.documentElement.style.setProperty("--router-tab-button-color",e.buttonColor??C.buttonColor),document.documentElement.style.setProperty("--router-tab-active-button-color",e.activeButtonColor??C.activeButtonColor),document.documentElement.style.setProperty("--router-tab-button-background",e.buttonBackground??C.buttonBackground),document.documentElement.style.setProperty("--router-tab-active-button-background",e.activeButtonBackground??C.activeButtonBackground),document.documentElement.style.setProperty("--router-tab-icon-color",e.iconColor??C.iconColor))}function ge(e){if(typeof document>"u")return;const o=document.documentElement,r=window.matchMedia(pe),s=()=>{o.dataset.theme=r.matches?"dark":"light"};N&&(r.removeEventListener("change",N),N=null),e==="system"?(s(),N=()=>s(),r.addEventListener("change",N)):o.dataset.theme=e}function he(e={}){if(typeof window>"u")return;const{styleKey:o=be,primaryKey:r=me,defaultStyle:s=He,defaultPrimary:i}=e,g=window.localStorage.getItem(o)??s;ge(g);const c=g==="dark"||g==="system"&&window.matchMedia(pe).matches?{...We}:{...C};i&&(c.primary=i);const f=Je(r);ne(f?{...c,...f}:c)}function qe(e,o){if(typeof window>"u")return;const r=o?.styleKey??be;window.localStorage.setItem(r,e),ge(e)}function Ge(e,o){if(typeof window>"u")return;const r=o?.primaryKey??me;window.localStorage.setItem(r,JSON.stringify(e)),ne(e)}function H(e,o){if(n.isRef(e)){const s=!n.isReadonly(e);return{value:e,update:s?i=>{e.value=i}:()=>{}}}if(typeof e=="function"){const s=e;return{value:n.computed(s),update:()=>{}}}const r=n.ref(e===void 0?o:e);return{value:r,update:s=>{r.value=s}}}function W(e={}){const o=H(e.title,"Untitled"),r=H(e.icon,""),s=H(e.closable,!0),i=H(e.meta,{});return{routeTabTitle:o.value,routeTabIcon:r.value,routeTabClosable:s.value,routeTabMeta:i.value,updateTitle:o.update,updateIcon:r.update,updateClosable:s.update,updateMeta:i.update}}function Xe(e,o="Page"){return W({title:n.computed(()=>e.value?"Loading...":o),icon:n.computed(()=>e.value?"mdi-loading mdi-spin":"mdi-page"),closable:n.computed(()=>!e.value)})}function Qe(e,o="Page",r="mdi-page"){return W({title:n.computed(()=>e.value>0?`${o} (${e.value})`:o),icon:n.computed(()=>e.value>0?"mdi-bell-badge":r)})}function Ze(e,o="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 W({title:n.computed(()=>o+r[e.value].suffix),icon:n.computed(()=>r[e.value].icon),closable:n.computed(()=>e.value!=="loading")})}let ye=!1;const et={install(e,o){if(ye)return;ye=!0;const{initTheme:r=!0,themeOptions:s,componentName:i=te.name||"RouterTab",tabsComponentName:g=F.name||"RouterTabs"}=o??{};r&&he(s??{}),e.component(i,te),e.component(g,F),g.toLowerCase()!=="router-tabs"&&e.component("router-tabs",F),Object.defineProperty(e.config.globalProperties,"$tabs",{configurable:!0,enumerable:!1,get(){return e._context.provides[j]},set(v){v&&e.provide(j,v)}})}};w.RouterTab=te,w.RouterTabs=F,w.default=et,w.initRouterTabsTheme=he,w.routerTabsKey=j,w.setRouterTabsPrimary=Ge,w.setRouterTabsTheme=qe,w.useLoadingTab=Xe,w.useNotificationTab=Qe,w.useReactiveTab=W,w.useRouterTabs=Z,w.useRouterTabsPersistence=ee,w.useStatusTab=Ze,Object.defineProperties(w,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
@@ -134,6 +134,7 @@ import {
134
134
  getCurrentInstance,
135
135
  nextTick,
136
136
  onBeforeUnmount,
137
+ onErrorCaptured,
137
138
  onMounted,
138
139
  provide,
139
140
  reactive,
@@ -275,119 +276,186 @@ export default defineComponent({
275
276
  function setupComponentWatching(routeKey: string, componentInstance: any) {
276
277
  if (!componentInstance || componentInstances.has(routeKey)) return
277
278
 
278
- // setup watchers for exposed reactive tab props
279
-
280
- componentInstances.set(routeKey, componentInstance)
281
-
282
- // Find the tab for this route
283
- const tab = controller.tabs.find(t => controller.getRouteKey(t.to) === routeKey)
284
- if (!tab) return
285
-
286
- const unwatchers: (() => void)[] = []
287
-
288
- // Watch routeTabTitle for title updates
289
- if (componentInstance.routeTabTitle !== undefined) {
290
- const unwatchTitle = watch(
291
- () => {
292
- // Handle both ref values and regular values
293
- const titleValue = componentInstance.routeTabTitle
294
- return titleValue && typeof titleValue === 'object' && 'value' in titleValue ? titleValue.value : titleValue
295
- },
296
- (newTitle) => {
297
- if (newTitle !== undefined && newTitle !== null) {
298
- const titleString = String(newTitle)
299
- tab.title = titleString
300
- triggerTabUpdate() // Trigger reactivity
301
- }
302
- },
303
- { immediate: true }
304
- )
305
- unwatchers.push(unwatchTitle)
306
- }
307
-
308
- // Watch routeTabIcon for icon updates
309
- if (componentInstance.routeTabIcon !== undefined) {
310
- const unwatchIcon = watch(
311
- () => {
312
- const iconValue = componentInstance.routeTabIcon
313
- return iconValue && typeof iconValue === 'object' && 'value' in iconValue ? iconValue.value : iconValue
314
- },
315
- (newIcon) => {
316
- if (newIcon !== undefined && newIcon !== null) {
317
- tab.icon = String(newIcon)
318
- triggerTabUpdate() // Trigger reactivity
319
- }
320
- },
321
- { immediate: true }
322
- )
323
- unwatchers.push(unwatchIcon)
324
- }
325
-
326
- // Watch routeTabClosable for closable state updates
327
- if (componentInstance.routeTabClosable !== undefined) {
328
- const unwatchClosable = watch(
329
- () => {
330
- const closableValue = componentInstance.routeTabClosable
331
- return closableValue && typeof closableValue === 'object' && 'value' in closableValue ? closableValue.value : closableValue
332
- },
333
- (newClosable) => {
334
- if (newClosable !== undefined && newClosable !== null) {
335
- tab.closable = Boolean(newClosable)
336
- triggerTabUpdate() // Trigger reactivity
337
- }
338
- },
339
- { immediate: true }
340
- )
341
- unwatchers.push(unwatchClosable)
342
- }
343
-
344
- // Watch routeTabMeta for general meta updates
345
- if (componentInstance.routeTabMeta !== undefined) {
346
- const unwatchMeta = watch(
347
- () => {
348
- const metaValue = componentInstance.routeTabMeta
349
- return metaValue && typeof metaValue === 'object' && 'value' in metaValue ? metaValue.value : metaValue
350
- },
351
- (newMeta) => {
352
- if (newMeta && typeof newMeta === 'object') {
353
- Object.assign(tab, newMeta)
354
- triggerTabUpdate() // Trigger reactivity
355
- }
356
- },
357
- { immediate: true, deep: true }
358
- )
359
- unwatchers.push(unwatchMeta)
279
+ try {
280
+ // setup watchers for exposed reactive tab props
281
+
282
+ componentInstances.set(routeKey, componentInstance)
283
+
284
+ // Find the tab for this route
285
+ const tab = controller.tabs.find(t => controller.getRouteKey(t.to) === routeKey)
286
+ if (!tab) {
287
+ console.warn(`[RouterTab] Cannot setup watching: tab not found for ${routeKey}`)
288
+ return
289
+ }
290
+
291
+ const unwatchers: (() => void)[] = []
292
+
293
+ // Watch routeTabTitle for title updates
294
+ if (componentInstance.routeTabTitle !== undefined) {
295
+ try {
296
+ const unwatchTitle = watch(
297
+ () => {
298
+ // Handle both ref values and regular values
299
+ const titleValue = componentInstance.routeTabTitle
300
+ return titleValue && typeof titleValue === 'object' && 'value' in titleValue ? titleValue.value : titleValue
301
+ },
302
+ (newTitle) => {
303
+ if (newTitle !== undefined && newTitle !== null) {
304
+ const titleString = String(newTitle)
305
+ tab.title = titleString
306
+ triggerTabUpdate() // Trigger reactivity
307
+ }
308
+ },
309
+ { immediate: true }
310
+ )
311
+ unwatchers.push(unwatchTitle)
312
+ } catch (error) {
313
+ console.error(`[RouterTab] Error watching routeTabTitle for ${routeKey}:`, error)
314
+ }
315
+ }
316
+
317
+ // Watch routeTabIcon for icon updates
318
+ if (componentInstance.routeTabIcon !== undefined) {
319
+ try {
320
+ const unwatchIcon = watch(
321
+ () => {
322
+ const iconValue = componentInstance.routeTabIcon
323
+ return iconValue && typeof iconValue === 'object' && 'value' in iconValue ? iconValue.value : iconValue
324
+ },
325
+ (newIcon) => {
326
+ if (newIcon !== undefined && newIcon !== null) {
327
+ tab.icon = String(newIcon)
328
+ triggerTabUpdate() // Trigger reactivity
329
+ }
330
+ },
331
+ { immediate: true }
332
+ )
333
+ unwatchers.push(unwatchIcon)
334
+ } catch (error) {
335
+ console.error(`[RouterTab] Error watching routeTabIcon for ${routeKey}:`, error)
336
+ }
337
+ }
338
+
339
+ // Watch routeTabClosable for closable state updates
340
+ if (componentInstance.routeTabClosable !== undefined) {
341
+ try {
342
+ const unwatchClosable = watch(
343
+ () => {
344
+ const closableValue = componentInstance.routeTabClosable
345
+ return closableValue && typeof closableValue === 'object' && 'value' in closableValue ? closableValue.value : closableValue
346
+ },
347
+ (newClosable) => {
348
+ if (newClosable !== undefined && newClosable !== null) {
349
+ tab.closable = Boolean(newClosable)
350
+ triggerTabUpdate() // Trigger reactivity
351
+ }
352
+ },
353
+ { immediate: true }
354
+ )
355
+ unwatchers.push(unwatchClosable)
356
+ } catch (error) {
357
+ console.error(`[RouterTab] Error watching routeTabClosable for ${routeKey}:`, error)
358
+ }
359
+ }
360
+
361
+ // Watch routeTabMeta for general meta updates
362
+ if (componentInstance.routeTabMeta !== undefined) {
363
+ try {
364
+ const unwatchMeta = watch(
365
+ () => {
366
+ const metaValue = componentInstance.routeTabMeta
367
+ return metaValue && typeof metaValue === 'object' && 'value' in metaValue ? metaValue.value : metaValue
368
+ },
369
+ (newMeta) => {
370
+ if (newMeta && typeof newMeta === 'object') {
371
+ Object.assign(tab, newMeta)
372
+ triggerTabUpdate() // Trigger reactivity
373
+ }
374
+ },
375
+ { immediate: true, deep: true }
376
+ )
377
+ unwatchers.push(unwatchMeta)
378
+ } catch (error) {
379
+ console.error(`[RouterTab] Error watching routeTabMeta for ${routeKey}:`, error)
380
+ }
381
+ }
382
+
383
+ watchedProperties.set(routeKey, unwatchers)
384
+ } catch (error) {
385
+ console.error(`[RouterTab] Error in setupComponentWatching for ${routeKey}:`, error)
386
+ // Clean up on error
387
+ cleanupComponentWatching(routeKey)
360
388
  }
361
-
362
- watchedProperties.set(routeKey, unwatchers)
363
389
  }
364
390
 
365
391
  // Cleanup watchers when component is unmounted
366
392
  function cleanupComponentWatching(routeKey: string) {
367
- const unwatchers = watchedProperties.get(routeKey)
368
- if (unwatchers) {
369
- unwatchers.forEach(unwatcher => unwatcher())
370
- watchedProperties.delete(routeKey)
393
+ try {
394
+ const unwatchers = watchedProperties.get(routeKey)
395
+ if (unwatchers) {
396
+ unwatchers.forEach(unwatcher => {
397
+ try {
398
+ unwatcher()
399
+ } catch (error) {
400
+ console.error(`[RouterTab] Error cleaning up watcher for ${routeKey}:`, error)
401
+ }
402
+ })
403
+ watchedProperties.delete(routeKey)
404
+ }
405
+ componentInstances.delete(routeKey)
406
+ } catch (error) {
407
+ console.error(`[RouterTab] Error in cleanupComponentWatching for ${routeKey}:`, error)
371
408
  }
372
- componentInstances.delete(routeKey)
373
409
  }
374
410
 
375
411
  // Handle component ref changes
376
412
  function handleComponentRef(el: any, routeKey: string) {
377
- if (el) {
378
- // Component mounted - set up watching
379
- // Check if properties are exposed directly on el (Vue 3 with defineExpose)
380
- if (el.routeTabTitle !== undefined || el.routeTabIcon !== undefined || el.routeTabClosable !== undefined) {
381
- setupComponentWatching(routeKey, el)
382
- } else if (el.$ && (el.$.routeTabTitle !== undefined || el.$.routeTabIcon !== undefined || el.$.routeTabClosable !== undefined)) {
383
- setupComponentWatching(routeKey, el.$)
413
+ try {
414
+ if (el) {
415
+ // Component mounted - set up watching
416
+ // Check if properties are exposed directly on el (Vue 3 with defineExpose)
417
+ if (el.routeTabTitle !== undefined || el.routeTabIcon !== undefined || el.routeTabClosable !== undefined) {
418
+ setupComponentWatching(routeKey, el)
419
+ } else if (el.$ && (el.$.routeTabTitle !== undefined || el.$.routeTabIcon !== undefined || el.$.routeTabClosable !== undefined)) {
420
+ setupComponentWatching(routeKey, el.$)
421
+ }
422
+ } else if (el === null) {
423
+ // Component unmounted - cleanup
424
+ cleanupComponentWatching(routeKey)
384
425
  }
385
- } else if (el === null) {
386
- // Component unmounted - cleanup
426
+ } catch (error) {
427
+ console.error(`[RouterTab] Error handling component ref for ${routeKey}:`, error)
428
+ // Clean up on error to prevent stale state
387
429
  cleanupComponentWatching(routeKey)
388
430
  }
389
431
  }
390
432
 
433
+ // Error handling: Capture errors from child components
434
+ onErrorCaptured((err, instance, info) => {
435
+ console.error('[RouterTab] Error captured from component:', err, info)
436
+
437
+ // Try to find the route key for the erroring component
438
+ if (instance && controller.activeId.value) {
439
+ const routeKey = controller.activeId.value
440
+
441
+ // Clean up the component instance to prevent stale state
442
+ cleanupComponentWatching(routeKey)
443
+
444
+ // Remove from KeepAlive cache if it's cached
445
+ const tab = controller.tabs.find(t => t.id === routeKey)
446
+ if (tab && tab.alive) {
447
+ console.warn(`[RouterTab] Removing errored component ${routeKey} from KeepAlive cache`)
448
+ tab.alive = false
449
+ nextTick(() => {
450
+ tab.alive = true
451
+ })
452
+ }
453
+ }
454
+
455
+ // Return false to propagate the error to parent
456
+ return false
457
+ })
458
+
391
459
  if (props.cookieKey !== null || props.persistence) {
392
460
  const options: RouterTabsPersistenceOptions = {
393
461
  ...(props.persistence ?? {})
@@ -862,7 +930,13 @@ export default defineComponent({
862
930
 
863
931
  // Cleanup all component watchers
864
932
  watchedProperties.forEach((unwatchers) => {
865
- unwatchers.forEach(unwatcher => unwatcher())
933
+ unwatchers.forEach(unwatcher => {
934
+ try {
935
+ unwatcher()
936
+ } catch (error) {
937
+ console.error('[RouterTab] Error during cleanup:', error)
938
+ }
939
+ })
866
940
  })
867
941
  watchedProperties.clear()
868
942
  componentInstances.clear()
@@ -880,6 +954,23 @@ export default defineComponent({
880
954
  () => hideContextMenu()
881
955
  )
882
956
 
957
+ // Clean up stale component instances when tabs are closed
958
+ watch(
959
+ () => controller.tabs.length,
960
+ () => {
961
+ // Check for stale component instances
962
+ const currentTabIds = new Set(controller.tabs.map(tab => tab.id))
963
+ const instanceKeys = Array.from(componentInstances.keys())
964
+
965
+ instanceKeys.forEach(key => {
966
+ if (!currentTabIds.has(key)) {
967
+ console.log(`[RouterTab] Cleaning up stale component instance: ${key}`)
968
+ cleanupComponentWatching(key)
969
+ }
970
+ })
971
+ }
972
+ )
973
+
883
974
  watch(
884
975
  () => props.contextmenu,
885
976
  value => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue3-router-tab",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",