vue3-router-tab 1.3.3 → 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.
- package/dist/vue3-router-tab.js +552 -498
- package/dist/vue3-router-tab.umd.cjs +1 -1
- package/lib/components/RouterTab.vue +217 -128
- package/package.json +1 -1
|
@@ -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 $(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 Be={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 x(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=Be[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:x(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 Pe(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:$(e,l);return{key:x(m),fullPath:m.fullPath,alive:G(m,a.keepAlive),reusable:X(m,!1),matched:m}}function E(l){const m=x(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=$(e,l),C=x(k),B=i.value===C;b==="sameTab"&&(b=B),b&&await M(C,!0),await e[m?"replace":"push"](k),B&&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),B=b||k||C;return B?B.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=$(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"?x(l):x($(e,l))}function D(){const l=s.find(m=>m.id===i.value);return{tabs:s.map(Pe),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=$(e,k.to),B=Ke(k),K=_(C,B,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=$(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),$e=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=$e}=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 xe=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 B(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(B(t),t)},enable:({target:t})=>B(t).some(r=>r.closable!==!1)}};function P(){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",P,{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){const f=t?.$el||t;D.value[r]=f??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"){P();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":P();break}}async function ke(t){t.disabled||(P(),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 key: ${r}. This may cause rendering issues.`),`${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",P)}),n.onBeforeUnmount(()=>{document.removeEventListener("keydown",P),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,()=>P()),n.watch(()=>e.contextmenu,t=>{t||P()}),n.watch(()=>U.value.length,t=>{T.visible&&t===0&&P()},{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:P,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(()=>[c.Component&&e.isTabReady(c.route)?(n.openBlock(),n.createElementBlock(n.Fragment,{key:0},[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},[(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"}))],1032,["include","max"])):(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"}))],64)):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(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),(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"}})}));
|
|
@@ -68,35 +68,33 @@
|
|
|
68
68
|
v-bind="pageTransitionProps"
|
|
69
69
|
appear
|
|
70
70
|
>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
:max="controller.options.maxAlive || undefined"
|
|
84
|
-
>
|
|
85
|
-
<component
|
|
86
|
-
:is="routerSlot.Component"
|
|
87
|
-
:key="getComponentCacheKey(routerSlot.route)"
|
|
88
|
-
:ref="(el: any) => handleComponentRef(el, controller.getRouteKey(routerSlot.route))"
|
|
89
|
-
class="router-tab-page"
|
|
90
|
-
/>
|
|
91
|
-
</KeepAlive>
|
|
71
|
+
<component
|
|
72
|
+
v-if="isRefreshing(routerSlot.route)"
|
|
73
|
+
:is="routerSlot.Component"
|
|
74
|
+
:key="getRefreshComponentKey(routerSlot.route)"
|
|
75
|
+
:ref="(el: any) => handleComponentRef(el, controller.getRouteKey(routerSlot.route))"
|
|
76
|
+
class="router-tab-page"
|
|
77
|
+
/>
|
|
78
|
+
<KeepAlive
|
|
79
|
+
v-else-if="controller.options.keepAlive"
|
|
80
|
+
:include="includeKeys"
|
|
81
|
+
:max="controller.options.maxAlive || undefined"
|
|
82
|
+
>
|
|
92
83
|
<component
|
|
93
|
-
v-
|
|
84
|
+
v-if="isTabReady(routerSlot.route)"
|
|
94
85
|
:is="routerSlot.Component"
|
|
95
86
|
:key="getComponentCacheKey(routerSlot.route)"
|
|
96
87
|
:ref="(el: any) => handleComponentRef(el, controller.getRouteKey(routerSlot.route))"
|
|
97
88
|
class="router-tab-page"
|
|
98
89
|
/>
|
|
99
|
-
</
|
|
90
|
+
</KeepAlive>
|
|
91
|
+
<component
|
|
92
|
+
v-else-if="isTabReady(routerSlot.route)"
|
|
93
|
+
:is="routerSlot.Component"
|
|
94
|
+
:key="getComponentCacheKey(routerSlot.route)"
|
|
95
|
+
:ref="(el: any) => handleComponentRef(el, controller.getRouteKey(routerSlot.route))"
|
|
96
|
+
class="router-tab-page"
|
|
97
|
+
/>
|
|
100
98
|
</transition>
|
|
101
99
|
</template>
|
|
102
100
|
</RouterView>
|
|
@@ -136,6 +134,7 @@ import {
|
|
|
136
134
|
getCurrentInstance,
|
|
137
135
|
nextTick,
|
|
138
136
|
onBeforeUnmount,
|
|
137
|
+
onErrorCaptured,
|
|
139
138
|
onMounted,
|
|
140
139
|
provide,
|
|
141
140
|
reactive,
|
|
@@ -277,119 +276,186 @@ export default defineComponent({
|
|
|
277
276
|
function setupComponentWatching(routeKey: string, componentInstance: any) {
|
|
278
277
|
if (!componentInstance || componentInstances.has(routeKey)) return
|
|
279
278
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
()
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
(
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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)
|
|
362
388
|
}
|
|
363
|
-
|
|
364
|
-
watchedProperties.set(routeKey, unwatchers)
|
|
365
389
|
}
|
|
366
390
|
|
|
367
391
|
// Cleanup watchers when component is unmounted
|
|
368
392
|
function cleanupComponentWatching(routeKey: string) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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)
|
|
373
408
|
}
|
|
374
|
-
componentInstances.delete(routeKey)
|
|
375
409
|
}
|
|
376
410
|
|
|
377
411
|
// Handle component ref changes
|
|
378
412
|
function handleComponentRef(el: any, routeKey: string) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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)
|
|
386
425
|
}
|
|
387
|
-
}
|
|
388
|
-
|
|
426
|
+
} catch (error) {
|
|
427
|
+
console.error(`[RouterTab] Error handling component ref for ${routeKey}:`, error)
|
|
428
|
+
// Clean up on error to prevent stale state
|
|
389
429
|
cleanupComponentWatching(routeKey)
|
|
390
430
|
}
|
|
391
431
|
}
|
|
392
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
|
+
|
|
393
459
|
if (props.cookieKey !== null || props.persistence) {
|
|
394
460
|
const options: RouterTabsPersistenceOptions = {
|
|
395
461
|
...(props.persistence ?? {})
|
|
@@ -612,10 +678,8 @@ export default defineComponent({
|
|
|
612
678
|
}
|
|
613
679
|
}
|
|
614
680
|
|
|
615
|
-
function setMenuItemRef(el: any, index: number) {
|
|
616
|
-
|
|
617
|
-
const element = el?.$el || el
|
|
618
|
-
menuItemRefs.value[index] = (element as HTMLElement) ?? null
|
|
681
|
+
function setMenuItemRef(el: any | null, index: number) {
|
|
682
|
+
menuItemRefs.value[index] = (el as HTMLElement) ?? null
|
|
619
683
|
}
|
|
620
684
|
|
|
621
685
|
function focusMenuItem(index: number) {
|
|
@@ -735,8 +799,10 @@ export default defineComponent({
|
|
|
735
799
|
const routeKey = controller.getRouteKey(route)
|
|
736
800
|
const tab = controller.tabs.find(item => item.id === routeKey)
|
|
737
801
|
|
|
802
|
+
// If tab doesn't exist yet, ensure it's created
|
|
738
803
|
if (!tab) {
|
|
739
|
-
|
|
804
|
+
// This shouldn't happen, but handle it gracefully
|
|
805
|
+
console.warn('[RouterTab] Tab not found for route:', routeKey)
|
|
740
806
|
return `${routeKey}::0`
|
|
741
807
|
}
|
|
742
808
|
|
|
@@ -789,7 +855,7 @@ export default defineComponent({
|
|
|
789
855
|
return controller.refreshingKey.value === controller.getRouteKey(route)
|
|
790
856
|
}
|
|
791
857
|
|
|
792
|
-
function isTabReady(route: RouteLocationNormalizedLoaded)
|
|
858
|
+
function isTabReady(route: RouteLocationNormalizedLoaded) {
|
|
793
859
|
const routeKey = controller.getRouteKey(route)
|
|
794
860
|
return controller.tabs.some(tab => tab.id === routeKey)
|
|
795
861
|
}
|
|
@@ -864,7 +930,13 @@ export default defineComponent({
|
|
|
864
930
|
|
|
865
931
|
// Cleanup all component watchers
|
|
866
932
|
watchedProperties.forEach((unwatchers) => {
|
|
867
|
-
unwatchers.forEach(unwatcher =>
|
|
933
|
+
unwatchers.forEach(unwatcher => {
|
|
934
|
+
try {
|
|
935
|
+
unwatcher()
|
|
936
|
+
} catch (error) {
|
|
937
|
+
console.error('[RouterTab] Error during cleanup:', error)
|
|
938
|
+
}
|
|
939
|
+
})
|
|
868
940
|
})
|
|
869
941
|
watchedProperties.clear()
|
|
870
942
|
componentInstances.clear()
|
|
@@ -882,6 +954,23 @@ export default defineComponent({
|
|
|
882
954
|
() => hideContextMenu()
|
|
883
955
|
)
|
|
884
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
|
+
|
|
885
974
|
watch(
|
|
886
975
|
() => props.contextmenu,
|
|
887
976
|
value => {
|