vue3-router-tab 1.2.1 → 1.2.3

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(b,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("vue"),require("vue-router")):typeof define=="function"&&define.amd?define(["exports","vue","vue-router"],t):(b=typeof globalThis<"u"?globalThis:b||self,t(b["vue3-router-tab"]={},b.Vue,b.VueRouter))})(this,(function(b,t,le){"use strict";function se(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 R(e,o){const i=e.resolve(o);if(!i||!i.matched.length)throw new Error(`[RouterTabs] Unable to resolve route: ${String(o)}`);return i}const re={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 C(e){const o=e.meta?.key;if(typeof o=="function"){const i=o(e);if(typeof i=="string"&&i.length)return i}else if(typeof o=="string"&&o.length){const i=re[o.toLowerCase()];return i?i(e):o}return e.fullPath}function M(e,o){const i=e.meta?.keepAlive;return typeof i=="boolean"?i:o}function L(e,o){const i=e.meta?.reuse;return typeof i=="boolean"?i:o}function H(e){const o=e.meta??{},i={};return"title"in o&&(i.title=o.title),"tips"in o&&(i.tips=o.tips),"icon"in o&&(i.icon=o.icon),"closable"in o&&(i.closable=o.closable),"tabClass"in o&&(i.tabClass=o.tabClass),"target"in o&&(i.target=o.target),"href"in o&&(i.href=o.href),i}function K(e,o,i){const n=H(e);return{id:C(e),to:e.fullPath,fullPath:e.fullPath,matched:e,alive:M(e,i),reusable:L(e,!1),closable:n.closable??!0,...n,...o}}function N(e,o,i,n){if(!e.find(d=>d.id===o.id)){if(i==="next"&&n){const d=e.findIndex(m=>m.id===n);if(d>-1){e.splice(d+1,0,o);return}}e.push(o)}}function Q(e,o,i){if(!o||o<=0)return;const n=e.filter(r=>r.alive);for(;n.length>o;){const r=n.shift();if(!r||r.id===i)continue;const d=e.findIndex(m=>m.id===r.id);d>-1&&(e[d].alive=!1)}}function ce(e){return{to:e.to,title:e.title,tips:e.tips,icon:e.icon,tabClass:e.tabClass,closable:e.closable}}function ue(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),o}function fe(e,o={}){const i=se(o),n=t.reactive([]),r=t.ref(null),d=t.shallowRef(),m=t.ref(null),s=t.computed(()=>n.filter(l=>l.alive).map(l=>l.id));let f=!1;function T(l){const c=typeof l.matched=="object"?l:R(e,l);return{key:C(c),fullPath:c.fullPath,alive:M(c,i.keepAlive),reusable:L(c,!1),matched:c}}function P(l){const c=C(l);let u=n.find(h=>h.id===c);return u?(u.fullPath=l.fullPath,u.to=l.fullPath,u.matched=l,u.alive=M(l,i.keepAlive),u.reusable=L(l,u.reusable),Object.assign(u,H(l)),u):(u=K(l,{},i.keepAlive),N(n,u,i.appendPosition,r.value),Q(n,i.maxAlive,r.value),u)}async function B(l,c=!1,u=!0){const h=R(e,l),w=C(h),a=r.value===w;u==="sameTab"&&(u=a),u&&await g(w,!0),await e[c?"replace":"push"](h),a&&await E()}function $(l){const c=n.findIndex(h=>h.id===l),u=n[c]||n[c-1]||n[0];return u?u.to:i.defaultRoute}async function v(l=r.value,c={}){if(l){if(!c.force&&i.keepLastTab&&n.length===1)throw new Error("[RouterTabs] Unable to close the final tab when keepLastTab is true.");if(await V(l,{force:c.force}),c.redirect!==null)if(r.value===l){const u=c.redirect??$(l);u&&await e.replace(u)}else c.redirect&&await e.replace(c.redirect)}}async function V(l,c={}){const u=n.findIndex(h=>h.id===l);u!==-1&&(n.splice(u,1),m.value===l&&(m.value=null),r.value===l&&(r.value=null,d.value=void 0))}async function g(l=r.value??void 0,c=!1){l&&(m.value=l,await t.nextTick(),c||await t.nextTick(),m.value=null)}async function O(l=!1){for(const c of n)await g(c.id,l)}async function F(l=i.defaultRoute){n.splice(0,n.length),r.value=null,d.value=void 0;for(const c of i.initialTabs){const u=R(e,c.to),h=K(u,c,i.keepAlive);n.push(h)}await e.replace(l)}async function E(){const l=r.value;l&&await g(l,!0)}function Y(l){return typeof l.matched=="object"?C(l):C(R(e,l))}function q(){const l=n.find(c=>c.id===r.value);return{tabs:n.map(ce),active:l?l.to:null}}async function S(l){f=!0,n.splice(0,n.length),r.value=null,d.value=void 0;const c=l?.tabs??[];for(const h of c)try{const w=R(e,h.to),a=ue(h),p=K(w,a,i.keepAlive);N(n,p,"last",null)}catch{}f=!1;const u=l?.active??c[c.length-1]?.to??i.defaultRoute;if(u)try{await e.replace(u)}catch{}}return t.watch(()=>e.currentRoute.value,l=>{if(f)return;const c=P(l);r.value=c.id,d.value=c,Q(n,i.maxAlive,r.value)},{immediate:!0}),i.initialTabs.length&&i.initialTabs.forEach(l=>{const c=R(e,l.to),u=K(c,l,i.keepAlive);N(n,u,"last",null)}),{options:i,tabs:n,activeId:r,current:d,includeKeys:s,refreshingKey:m,openTab:B,closeTab:v,removeTab:V,refreshTab:g,refreshAll:O,reset:F,reload:E,getRouteKey:Y,matchRoute:T,snapshot:q,hydrate:S}}function W(e){return e?typeof e=="string"?{name:e}:e:{}}const A=Symbol("RouterTabsContext"),x="router-tabs:snapshot";function j(e={}){const{optional:o=!1}=e,i=t.inject(A,null);if(i)return i;const n=t.inject("$tabs",null);if(n)return n;const d=t.getCurrentInstance()?.appContext.config.globalProperties.$tabs;if(d)return d;if(!o)throw new Error("[RouterTabs] useRouterTabs must be used within <router-tab>.");return null}const de=864e5;function pe(e){if(typeof document>"u")return null;const o=`${encodeURIComponent(e)}=`,i=document.cookie?document.cookie.split("; "):[];for(const n of i)if(n.startsWith(o))return decodeURIComponent(n.slice(o.length));return null}function X(e,o,i){if(typeof document>"u")return;const{expiresInDays:n=7,path:r="/",domain:d,secure:m,sameSite:s="lax"}=i,f=[`${encodeURIComponent(e)}=${encodeURIComponent(o)}`];if(n!==1/0){const T=new Date(Date.now()+n*de).toUTCString();f.push(`Expires=${T}`)}r&&f.push(`Path=${r}`),d&&f.push(`Domain=${d}`),m&&f.push("Secure"),s&&f.push(`SameSite=${s.charAt(0).toUpperCase()}${s.slice(1)}`),document.cookie=f.join("; ")}function Z(e,o){if(typeof document>"u")return;const{path:i="/",domain:n}=o,r=[`${encodeURIComponent(e)}=`];r.push("Expires=Thu, 01 Jan 1970 00:00:01 GMT"),i&&r.push(`Path=${i}`),n&&r.push(`Domain=${n}`),document.cookie=r.join("; ")}const me=e=>JSON.stringify(e??null),be=e=>{if(!e)return null;try{return JSON.parse(e)}catch{return null}};function D(e={}){const{cookieKey:o=x,serialize:i=me,deserialize:n=be}=e,r=j({optional:!0}),d=t.ref(!0),m=s=>{t.onMounted(async()=>{const f=n(pe(o));if(f&&f.tabs?.length)try{d.value=!0,await s.hydrate(f)}finally{d.value=!1}else try{d.value=!0;const P=e.fallbackRoute??s.options.defaultRoute;await s.reset(P)}finally{d.value=!1}const T=s.snapshot();T.tabs.length?X(o,i(T),e):Z(o,e),d.value=!1}),t.watch(()=>({tabs:s.tabs.map(f=>({to:f.to,title:f.title,tips:f.tips,icon:f.icon,tabClass:f.tabClass,closable:f.closable})),active:s.activeId.value}),()=>{if(d.value)return;const f=s.snapshot();f.tabs.length?X(o,i(f),e):Z(o,e)},{deep:!0})};r?m(r):t.onMounted(()=>{const s=j({optional:!0});s&&m(s)})}const he=t.defineComponent({name:"RouterTab",components:{RouterView:le.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:x},persistence:{type:Object,default:null}},setup(e){const o=t.getCurrentInstance();if(!o)throw new Error("[RouterTab] component must be used within a Vue application context.");const i=o.appContext.app.config.globalProperties.$router;if(!i)throw new Error("[RouterTab] Vue Router is required. Make sure to call app.use(router) before RouterTab.");const n=fe(i,{initialTabs:e.tabs,keepAlive:e.keepAlive,maxAlive:e.maxAlive,keepLastTab:e.keepLastTab,appendPosition:e.append,defaultRoute:e.defaultPage});t.provide(A,n),o.appContext.config.globalProperties.$tabs=n;const r=t.computed(()=>!!o?.slots?.default);if(e.cookieKey!==null||e.persistence){const a={...e.persistence??{}};e.cookieKey!==null?a.cookieKey=e.cookieKey||x:a.cookieKey||(a.cookieKey=x),D(a)}const d=t.computed(()=>W(e.tabTransition)),m=t.computed(()=>W(e.pageTransition)),s=t.reactive({visible:!1,target:null,position:{x:0,y:0}}),f=["refresh","refreshAll","close","closeLefts","closeRights","closeOthers"];function T(a){return n.tabs.findIndex(p=>p.id===a)}function P(a){const p=T(a.id);return p>0?n.tabs.slice(0,p):[]}function B(a){const p=T(a.id);return p>-1?n.tabs.slice(p+1):[]}function $(a){return n.tabs.filter(p=>p.id!==a.id)}async function v(a,p){const y=a.filter(k=>k.closable!==!1);if(y.length){for(const k of y)n.activeId.value===k.id?await n.closeTab(k.id,{redirect:p.to,force:!0}):await n.removeTab(k.id,{force:!0});n.activeId.value!==p.id&&await n.openTab(p.to,!0,!1)}}const V={refresh:{label:"Refresh",handler:async({target:a})=>{await n.refreshTab(a.id,!0)}},refreshAll:{label:"Refresh All",handler:async()=>{await n.refreshAll(!0)}},close:{label:"Close",handler:async({target:a})=>{await n.closeTab(a.id)},enable:({target:a})=>S(a)},closeLefts:{label:"Close to the Left",handler:async({target:a})=>{await v(P(a),a)},enable:({target:a})=>P(a).some(p=>p.closable!==!1)},closeRights:{label:"Close to the Right",handler:async({target:a})=>{await v(B(a),a)},enable:({target:a})=>B(a).some(p=>p.closable!==!1)},closeOthers:{label:"Close Others",handler:async({target:a})=>{await v($(a),a)},enable:({target:a})=>$(a).some(p=>p.closable!==!1)}};function g(){s.visible=!1,s.target=null}function O(a,p){e.contextmenu&&(s.visible=!0,s.target=a,s.position.x=p.clientX,s.position.y=p.clientY,document.addEventListener("click",g,{once:!0}))}function F(a,p){const y=typeof a=="string"?{id:a}:a,k=V[y.id],Ve=y.label??k?.label??String(y.id),G=y.visible??k?.visible??!0;if(!(typeof G=="function"?G(p):G!==!1))return null;const J=y.enable??k?.enable??!0,Me=typeof J=="function"?J(p):J!==!1,ae=y.handler??k?.handler;if(!ae)return null;const Le=async()=>{await Promise.resolve(ae(p))};return{id:String(y.id),label:Ve,disabled:!Me,action:Le}}const E=t.computed(()=>{if(!s.visible||!s.target||e.contextmenu===!1)return[];const a=Array.isArray(e.contextmenu)?e.contextmenu:f,p={target:s.target,controller:n};return a.map(y=>F(y,p)).filter(y=>!!y)});async function Y(a){a.disabled||(g(),await a.action())}function q(a){return typeof a.title=="string"?a.title:Array.isArray(a.title)&&a.title.length?String(a.title[0]):a.fullPath}function S(a){return!(a.closable===!1||n.options.keepLastTab&&n.tabs.length<=1)}async function l(a){await n.closeTab(a.id)}function c(a){n.activeId.value!==a.id&&n.openTab(a.to,!1)}function u(a){return["router-tab__item",{"is-active":n.activeId.value===a.id,"is-closable":S(a)},a.tabClass]}function h(a){return n.refreshingKey.value===n.getRouteKey(a)}t.onMounted(()=>{document.addEventListener("keydown",g)}),t.onBeforeUnmount(()=>{document.removeEventListener("keydown",g),o.appContext.config.globalProperties.$tabs=null}),t.watch(()=>e.keepAlive,a=>{n.options.keepAlive=a}),t.watch(()=>n.activeId.value,()=>g()),t.watch(()=>e.contextmenu,a=>{a||g()}),t.watch(()=>E.value.length,a=>{s.visible&&a===0&&g()});const w=n.includeKeys;return{controller:n,tabs:n.tabs,includeKeys:w,tabTransitionProps:d,pageTransitionProps:m,buildTabClass:u,activate:c,close:l,context:s,menuItems:E,handleMenuAction:Y,showContextMenu:O,hideContextMenu:g,tabTitle:q,isClosable:S,isRefreshing:h,hasCustomSlot:r}}}),ye=(e,o)=>{const i=e.__vccOpts||e;for(const[n,r]of o)i[n]=r;return i},ge={class:"router-tab"},ke={class:"router-tab__header"},Te={class:"router-tab__slot-start"},we={class:"router-tab__scroll"},Re=["onClick","onAuxclick","onContextmenu"],Ce=["title"],Pe=["onClick"],Ae={class:"router-tab__slot-end"},_e={class:"router-tab__container"},ve=["aria-disabled","onClick"];function Ee(e,o,i,n,r,d){const m=t.resolveComponent("RouterView");return t.openBlock(),t.createElementBlock("div",ge,[t.createElementVNode("header",ke,[t.createElementVNode("div",Te,[t.renderSlot(e.$slots,"start")]),t.createElementVNode("div",we,[t.createVNode(t.TransitionGroup,t.mergeProps({tag:"ul",class:"router-tab__nav"},e.tabTransitionProps),{default:t.withCtx(()=>[(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(e.tabs,s=>(t.openBlock(),t.createElementBlock("li",{key:s.id,class:t.normalizeClass(e.buildTabClass(s)),onClick:f=>e.activate(s),onAuxclick:t.withModifiers(f=>e.close(s),["middle","prevent"]),onContextmenu:t.withModifiers(f=>e.showContextMenu(s,f),["prevent"])},[t.createElementVNode("span",{class:"router-tab__item-title",title:e.tabTitle(s)},[s.icon?(t.openBlock(),t.createElementBlock("i",{key:0,class:t.normalizeClass(["router-tab__item-icon",s.icon])},null,2)):t.createCommentVNode("",!0),t.createTextVNode(" "+t.toDisplayString(e.tabTitle(s)),1)],8,Ce),e.isClosable(s)?(t.openBlock(),t.createElementBlock("a",{key:0,class:"router-tab__item-close",type:"button",onClick:t.withModifiers(f=>e.close(s),["stop"])},null,8,Pe)):t.createCommentVNode("",!0)],42,Re))),128))]),_:1},16)]),t.createElementVNode("div",Ae,[t.renderSlot(e.$slots,"end")])]),t.createElementVNode("div",_e,[t.createVNode(m,null,{default:t.withCtx(s=>[e.hasCustomSlot?t.renderSlot(e.$slots,"default",t.normalizeProps(t.mergeProps({key:0},{...s,controller:e.controller}))):(t.openBlock(),t.createElementBlock(t.Fragment,{key:1},[t.createVNode(t.Transition,t.mergeProps(e.pageTransitionProps,{appear:""}),{default:t.withCtx(()=>[e.controller.options.keepAlive?(t.openBlock(),t.createBlock(t.KeepAlive,{key:0,include:e.includeKeys,max:e.controller.options.maxAlive||void 0},[e.isRefreshing(s.route)?t.createCommentVNode("",!0):(t.openBlock(),t.createBlock(t.resolveDynamicComponent(s.Component),{key:e.controller.getRouteKey(s.route),class:"router-tab-page"}))],1032,["include","max"])):t.createCommentVNode("",!0)]),_:2},1040),t.createVNode(t.Transition,t.mergeProps(e.pageTransitionProps,{appear:""}),{default:t.withCtx(()=>[!e.controller.options.keepAlive||e.isRefreshing(s.route)?(t.openBlock(),t.createBlock(t.resolveDynamicComponent(s.Component),{key:e.controller.getRouteKey(s.route)+(e.isRefreshing(s.route)?"-refresh":""),class:"router-tab-page"})):t.createCommentVNode("",!0)]),_:2},1040)],64))]),_:3})]),e.context.visible&&e.context.target?(t.openBlock(),t.createElementBlock("div",{key:0,class:"router-tab__contextmenu",style:t.normalizeStyle({left:e.context.position.x+"px",top:e.context.position.y+"px"})},[(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(e.menuItems,s=>(t.openBlock(),t.createElementBlock("a",{key:s.id,class:"router-tab__contextmenu-item","aria-disabled":s.disabled,onClick:t.withModifiers(f=>e.handleMenuAction(s),["prevent"])},t.toDisplayString(s.label),9,ve))),128))],4)):t.createCommentVNode("",!0)])}const U=ye(he,[["render",Ee]]),Se={class:"router-tabs","aria-hidden":"true"},I=t.defineComponent({name:"RouterTabs",__name:"RouterTabs",props:{cookieKey:{},expiresInDays:{},path:{},domain:{},secure:{type:Boolean},sameSite:{},serialize:{type:Function},deserialize:{type:Function},fallbackRoute:{}},setup(e){return D(e),(i,n)=>(t.openBlock(),t.createElementBlock("span",Se))}}),ee="tab-theme-style",te="tab-theme-primary-color",Ke="system",xe="#635bff",Ie="(prefers-color-scheme: dark)";let _=null;function ne(e){typeof document>"u"||(document.documentElement.style.setProperty("--theme-primary",e),document.documentElement.style.setProperty("--router-tab-primary",e))}function oe(e){if(typeof document>"u")return;const o=document.documentElement,i=window.matchMedia(Ie),n=()=>{o.dataset.theme=i.matches?"dark":"light"};_&&(i.removeEventListener("change",_),_=null),e==="system"?(n(),_=()=>n(),i.addEventListener("change",_)):o.dataset.theme=e}function ie(e={}){if(typeof window>"u")return;const{styleKey:o=ee,primaryKey:i=te,defaultStyle:n=Ke,defaultPrimary:r=xe}=e,d=window.localStorage.getItem(o)??n,m=window.localStorage.getItem(i)??r;oe(d),ne(m)}function Be(e,o){if(typeof window>"u")return;const i=o?.styleKey??ee;window.localStorage.setItem(i,e),oe(e)}function $e(e,o){if(typeof window>"u")return;const i=o?.primaryKey??te;window.localStorage.setItem(i,e),ne(e)}const z={install(e){if(z._installed)return;z._installed=!0,ie();const o=U.name||"RouterTab",i=I.name||"RouterTabs";e.component(o,U),e.component(i,I),i!=="router-tabs"&&e.component("router-tabs",I),Object.defineProperty(e.config.globalProperties,"$tabs",{configurable:!0,enumerable:!1,get(){return e._context.provides[A]},set(n){n&&e.provide(A,n)}})}};b.RouterTab=U,b.RouterTabs=I,b.default=z,b.initRouterTabsTheme=ie,b.routerTabsKey=A,b.setRouterTabsPrimary=$e,b.setRouterTabsTheme=Be,b.useRouterTabs=j,b.useRouterTabsPersistence=D,Object.defineProperties(b,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
1
+ (function(y,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("vue"),require("vue-router")):typeof define=="function"&&define.amd?define(["exports","vue","vue-router"],t):(y=typeof globalThis<"u"?globalThis:y||self,t(y["vue3-router-tab"]={},y.Vue,y.VueRouter))})(this,(function(y,t,ce){"use strict";function ue(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 P(e,a){const n=e.resolve(a);if(!n||!n.matched.length)throw new Error(`[RouterTabs] Unable to resolve route: ${String(a)}`);return n}const de={path:e=>e.path,fullpath:e=>e.fullPath,fullname:e=>e.fullPath,full:e=>e.fullPath,name:e=>e.name?String(e.name):e.fullPath};function E(e){const a=e.meta?.key;if(typeof a=="function"){const n=a(e);if(typeof n=="string"&&n.length)return n}else if(typeof a=="string"&&a.length){const n=de[a.toLowerCase()];return n?n(e):a}return e.fullPath}function N(e,a){const n=e.meta?.keepAlive;return typeof n=="boolean"?n:a}function j(e,a){const n=e.meta?.reuse;return typeof n=="boolean"?n:a}function Z(e){const a=e.meta??{},n={};return"title"in a&&(n.title=a.title),"tips"in a&&(n.tips=a.tips),"icon"in a&&(n.icon=a.icon),"closable"in a&&(n.closable=a.closable),"tabClass"in a&&(n.tabClass=a.tabClass),"target"in a&&(n.target=a.target),"href"in a&&(n.href=a.href),n}function S(e,a,n){const i=Z(e);return{id:E(e),to:e.fullPath,fullPath:e.fullPath,matched:e,alive:N(e,n),reusable:j(e,!1),closable:i.closable??!0,...i,...a}}function O(e,a,n,i){if(!e.find(m=>m.id===a.id)){if(n==="next"&&i){const m=e.findIndex(g=>g.id===i);if(m>-1){e.splice(m+1,0,a);return}}e.push(a)}}function ee(e,a,n){if(!a||a<=0)return;const i=e.filter(r=>r.alive);for(;i.length>a;){const r=i.shift();if(!r||r.id===n)continue;const m=e.findIndex(g=>g.id===r.id);m>-1&&(e[m].alive=!1)}}function fe(e){return{to:e.to,title:e.title,tips:e.tips,icon:e.icon,tabClass:e.tabClass,closable:e.closable}}function be(e){const a={};return"title"in e&&(a.title=e.title),"tips"in e&&(a.tips=e.tips),"icon"in e&&(a.icon=e.icon),"tabClass"in e&&(a.tabClass=e.tabClass),"closable"in e&&(a.closable=e.closable),a}function me(e,a={}){const n=ue(a),i=t.reactive([]),r=t.ref(null),m=t.shallowRef(),g=t.ref(null),s=t.computed(()=>i.filter(l=>l.alive).map(l=>l.id));let c=!1;function u(l){const d=typeof l.matched=="object"?l:P(e,l);return{key:E(d),fullPath:d.fullPath,alive:N(d,n.keepAlive),reusable:j(d,!1),matched:d}}function C(l){const d=E(l);let b=i.find(h=>h.id===d);return b?(b.fullPath=l.fullPath,b.to=l.fullPath,b.matched=l,b.alive=N(l,n.keepAlive),b.reusable=j(l,b.reusable),Object.assign(b,Z(l)),b):(b=S(l,{},n.keepAlive),O(i,b,n.appendPosition,r.value),ee(i,n.maxAlive,r.value),b)}async function R(l,d=!1,b=!0){const h=P(e,l),B=E(h),A=r.value===B;b==="sameTab"&&(b=A),b&&await w(B,!0),await e[d?"replace":"push"](h),A&&await V()}function $(l){const d=i.findIndex(h=>h.id===l),b=i[d]||i[d-1]||i[0];return b?b.to:n.defaultRoute}async function L(l=r.value,d={}){if(l){if(!d.force&&n.keepLastTab&&i.length===1)throw new Error("[RouterTabs] Unable to close the final tab when keepLastTab is true.");if(await D(l,{force:d.force}),d.redirect!==null)if(r.value===l){const b=d.redirect??$(l);b&&await e.replace(b)}else d.redirect&&await e.replace(d.redirect)}}async function D(l,d={}){const b=i.findIndex(h=>h.id===l);b!==-1&&(i.splice(b,1),g.value===l&&(g.value=null),r.value===l&&(r.value=null,m.value=void 0))}async function w(l=r.value??void 0,d=!1){l&&(g.value=l,await t.nextTick(),d||await t.nextTick(),g.value=null)}async function q(l=!1){for(const d of i)await w(d.id,l)}async function T(l=n.defaultRoute){i.splice(0,i.length),r.value=null,m.value=void 0;for(const d of n.initialTabs){const b=P(e,d.to),h=S(b,d,n.keepAlive);i.push(h)}await e.replace(l)}async function V(){const l=r.value;l&&await w(l,!0)}function G(l){return typeof l.matched=="object"?E(l):E(P(e,l))}function M(){const l=i.find(d=>d.id===r.value);return{tabs:i.map(fe),active:l?l.to:null}}async function H(l){c=!0,i.splice(0,i.length),r.value=null,m.value=void 0;const d=l?.tabs??[];for(const h of d)try{const B=P(e,h.to),A=be(h),Q=S(B,A,n.keepAlive);O(i,Q,"last",null)}catch{}c=!1;const b=l?.active??d[d.length-1]?.to??n.defaultRoute;if(b)try{await e.replace(b)}catch{}}return t.watch(()=>e.currentRoute.value,l=>{if(c)return;const d=C(l);r.value=d.id,m.value=d,ee(i,n.maxAlive,r.value)},{immediate:!0}),n.initialTabs.length&&n.initialTabs.forEach(l=>{const d=P(e,l.to),b=S(d,l,n.keepAlive);O(i,b,"last",null)}),{options:n,tabs:i,activeId:r,current:m,includeKeys:s,refreshingKey:g,openTab:R,closeTab:L,removeTab:D,refreshTab:w,refreshAll:q,reset:T,reload:V,getRouteKey:G,matchRoute:u,snapshot:M,hydrate:H}}function te(e){return e?typeof e=="string"?{name:e}:e:{}}const I=Symbol("RouterTabsContext"),K="router-tabs:snapshot";function z(e={}){const{optional:a=!1}=e,n=t.inject(I,null);if(n)return n;const i=t.inject("$tabs",null);if(i)return i;const m=t.getCurrentInstance()?.appContext.config.globalProperties.$tabs;if(m)return m;if(!a)throw new Error("[RouterTabs] useRouterTabs must be used within <router-tab>.");return null}const pe=864e5;function ge(e){if(typeof document>"u")return null;const a=`${encodeURIComponent(e)}=`,n=document.cookie?document.cookie.split("; "):[];for(const i of n)if(i.startsWith(a))return decodeURIComponent(i.slice(a.length));return null}function ne(e,a,n){if(typeof document>"u")return;const{expiresInDays:i=7,path:r="/",domain:m,secure:g,sameSite:s="lax"}=n,c=[`${encodeURIComponent(e)}=${encodeURIComponent(a)}`];if(i!==1/0){const u=new Date(Date.now()+i*pe).toUTCString();c.push(`Expires=${u}`)}r&&c.push(`Path=${r}`),m&&c.push(`Domain=${m}`),g&&c.push("Secure"),s&&c.push(`SameSite=${s.charAt(0).toUpperCase()}${s.slice(1)}`),document.cookie=c.join("; ")}function oe(e,a){if(typeof document>"u")return;const{path:n="/",domain:i}=a,r=[`${encodeURIComponent(e)}=`];r.push("Expires=Thu, 01 Jan 1970 00:00:01 GMT"),n&&r.push(`Path=${n}`),i&&r.push(`Domain=${i}`),document.cookie=r.join("; ")}const ye=e=>JSON.stringify(e??null),he=e=>{if(!e)return null;try{return JSON.parse(e)}catch{return null}};function U(e={}){const{cookieKey:a=K,serialize:n=ye,deserialize:i=he}=e,r=z({optional:!0}),m=t.ref(!0),g=s=>{t.onMounted(async()=>{const c=i(ge(a));if(c&&c.tabs?.length)try{if(m.value=!0,await s.hydrate(c),c.active){await t.nextTick();const C=s.tabs.find(R=>R.to===c.active);C&&(s.activeId.value=C.id,s.current.value=C)}}finally{m.value=!1}else try{m.value=!0;const C=e.fallbackRoute??s.options.defaultRoute;await s.reset(C)}finally{m.value=!1}const u=s.snapshot();u.tabs.length?ne(a,n(u),e):oe(a,e),m.value=!1}),t.watch(()=>({tabs:s.tabs.map(c=>({to:c.to,title:c.title,tips:c.tips,icon:c.icon,tabClass:c.tabClass,closable:c.closable})),active:s.activeId.value}),()=>{if(m.value)return;const c=s.snapshot();c.tabs.length?ne(a,n(c),e):oe(a,e)},{deep:!0})};r?g(r):t.onMounted(()=>{const s=z({optional:!0});s&&g(s)})}const ke=t.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:K},persistence:{type:Object,default:null},sortable:{type:Boolean,default:!0},titleResolver:{type:Function,default:null}},emits:["tab-sort","tab-sorted"],setup(e,{emit:a}){const n=t.getCurrentInstance();if(!n)throw new Error("[RouterTab] component must be used within a Vue application context.");const i=n.appContext.app.config.globalProperties.$router;if(!i)throw new Error("[RouterTab] Vue Router is required. Make sure to call app.use(router) before RouterTab.");const r=me(i,{initialTabs:e.tabs,keepAlive:e.keepAlive,maxAlive:e.maxAlive,keepLastTab:e.keepLastTab,appendPosition:e.append,defaultRoute:e.defaultPage});t.provide(I,r),n.appContext.config.globalProperties.$tabs=r;const m=t.computed(()=>!!n?.slots?.default);if(e.cookieKey!==null||e.persistence){const o={...e.persistence??{}};e.cookieKey!==null?o.cookieKey=e.cookieKey||K:o.cookieKey||(o.cookieKey=K),U(o)}const g=t.computed(()=>te(e.tabTransition)),s=t.computed(()=>te(e.pageTransition)),c=t.reactive({visible:!1,target:null,position:{x:0,y:0}}),u=t.reactive({dragging:!1,dragIndex:-1,dropIndex:-1,dragTab:null}),C=["refresh","refreshAll","close","closeLefts","closeRights","closeOthers"];function R(o){return r.tabs.findIndex(f=>f.id===o)}function $(o){const f=R(o.id);return f>0?r.tabs.slice(0,f):[]}function L(o){const f=R(o.id);return f>-1?r.tabs.slice(f+1):[]}function D(o){return r.tabs.filter(f=>f.id!==o.id)}async function w(o,f){const p=o.filter(v=>v.closable!==!1);if(p.length){for(const v of p)r.activeId.value===v.id?await r.closeTab(v.id,{redirect:f.to,force:!0}):await r.removeTab(v.id,{force:!0});r.activeId.value!==f.id&&await r.openTab(f.to,!0,!1)}}const q={refresh:{label:"Refresh",handler:async({target:o})=>{await r.refreshTab(o.id,!0)}},refreshAll:{label:"Refresh All",handler:async()=>{await r.refreshAll(!0)}},close:{label:"Close",handler:async({target:o})=>{await r.closeTab(o.id)},enable:({target:o})=>d(o)},closeLefts:{label:"Close to the Left",handler:async({target:o})=>{await w($(o),o)},enable:({target:o})=>$(o).some(f=>f.closable!==!1)},closeRights:{label:"Close to the Right",handler:async({target:o})=>{await w(L(o),o)},enable:({target:o})=>L(o).some(f=>f.closable!==!1)},closeOthers:{label:"Close Others",handler:async({target:o})=>{await w(D(o),o)},enable:({target:o})=>D(o).some(f=>f.closable!==!1)}};function T(){c.visible=!1,c.target=null}function V(o,f){e.contextmenu&&(c.visible=!0,c.target=o,c.position.x=f.clientX,c.position.y=f.clientY,document.addEventListener("click",T,{once:!0}))}function G(o,f){const p=typeof o=="string"?{id:o}:o,v=q[p.id],Fe=p.label??v?.label??String(p.id),W=p.visible??v?.visible??!0;if(!(typeof W=="function"?W(f):W!==!1))return null;const X=p.enable??v?.enable??!0,Ye=typeof X=="function"?X(f):X!==!1,se=p.handler??v?.handler;if(!se)return null;const Je=async()=>{await Promise.resolve(se(f))};return{id:String(p.id),label:Fe,disabled:!Ye,action:Je}}const M=t.computed(()=>{if(!c.visible||!c.target||e.contextmenu===!1)return[];const o=Array.isArray(e.contextmenu)?e.contextmenu:C,f={target:c.target,controller:r};return o.map(p=>G(p,f)).filter(p=>!!p)});async function H(o){o.disabled||(T(),await o.action())}function l(o){return e.titleResolver?e.titleResolver(o):typeof o.title=="string"?o.title:Array.isArray(o.title)&&o.title.length?String(o.title[0]):o.fullPath}function d(o){return!(o.closable===!1||r.options.keepLastTab&&r.tabs.length<=1)}async function b(o){await r.closeTab(o.id)}function h(o){r.activeId.value!==o.id&&r.openTab(o.to,!1)}function B(o){return["router-tab__item",{"is-active":r.activeId.value===o.id,"is-closable":d(o),"is-dragging":u.dragging&&u.dragTab?.id===o.id,"is-drag-over":u.dropIndex===R(o.id)},o.tabClass]}function A(o){return r.refreshingKey.value===r.getRouteKey(o)}function Q(o,f,p){e.sortable&&(u.dragging=!0,u.dragIndex=f,u.dragTab=o,p.dataTransfer&&(p.dataTransfer.effectAllowed="move",p.dataTransfer.setData("text/plain",o.id)),a("tab-sort",{tab:o,index:f}))}function Ne(o,f){!e.sortable||!u.dragging||(f.preventDefault(),f.dataTransfer&&(f.dataTransfer.dropEffect="move"))}function je(o){!e.sortable||!u.dragging||(u.dropIndex=o)}function Oe(){!e.sortable||u.dragging}function ze(o,f){if(!(!e.sortable||!u.dragging)){if(f.preventDefault(),u.dragIndex!==-1&&u.dragIndex!==o){const p=r.tabs.splice(u.dragIndex,1)[0];r.tabs.splice(o,0,p),a("tab-sorted",{tab:p,fromIndex:u.dragIndex,toIndex:o})}le()}}function le(){u.dragging=!1,u.dragIndex=-1,u.dropIndex=-1,u.dragTab=null}t.onMounted(()=>{document.addEventListener("keydown",T)}),t.onBeforeUnmount(()=>{document.removeEventListener("keydown",T),n.appContext.config.globalProperties.$tabs=null}),t.watch(()=>e.keepAlive,o=>{r.options.keepAlive=o}),t.watch(()=>r.activeId.value,()=>T()),t.watch(()=>e.contextmenu,o=>{o||T()}),t.watch(()=>M.value.length,o=>{c.visible&&o===0&&T()});const Ue=r.includeKeys;return{controller:r,tabs:r.tabs,includeKeys:Ue,tabTransitionProps:g,pageTransitionProps:s,buildTabClass:B,activate:h,close:b,context:c,menuItems:M,handleMenuAction:H,showContextMenu:V,hideContextMenu:T,getTabTitle:l,isClosable:d,isRefreshing:A,hasCustomSlot:m,onDragStart:Q,onDragOver:Ne,onDragEnter:je,onDragLeave:Oe,onDrop:ze,onDragEnd:le}}}),Te=(e,a)=>{const n=e.__vccOpts||e;for(const[i,r]of a)n[i]=r;return n},ve={class:"router-tab"},Ce={class:"router-tab__header"},we={class:"router-tab__slot-start"},Re={class:"router-tab__scroll"},Be=["data-title","draggable","onClick","onAuxclick","onContextmenu","onDragstart","onDragover","onDragenter","onDrop"],Pe=["title"],Ee=["onClick"],Ae={class:"router-tab__slot-end"},Ie={class:"router-tab__container"},xe=["aria-disabled","onClick"];function De(e,a,n,i,r,m){const g=t.resolveComponent("RouterView");return t.openBlock(),t.createElementBlock("div",ve,[t.createElementVNode("header",Ce,[t.createElementVNode("div",we,[t.renderSlot(e.$slots,"start")]),t.createElementVNode("div",Re,[t.createVNode(t.TransitionGroup,t.mergeProps({tag:"ul",class:"router-tab__nav"},e.tabTransitionProps),{default:t.withCtx(()=>[(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(e.tabs,(s,c)=>(t.openBlock(),t.createElementBlock("li",{key:s.id,class:t.normalizeClass(e.buildTabClass(s)),"data-title":e.getTabTitle(s),draggable:e.sortable,onClick:u=>e.activate(s),onAuxclick:t.withModifiers(u=>e.close(s),["middle","prevent"]),onContextmenu:t.withModifiers(u=>e.showContextMenu(s,u),["prevent"]),onDragstart:u=>e.onDragStart(s,c,u),onDragover:u=>e.onDragOver(c,u),onDragenter:u=>e.onDragEnter(c),onDragleave:a[0]||(a[0]=(...u)=>e.onDragLeave&&e.onDragLeave(...u)),onDrop:u=>e.onDrop(c,u),onDragend:a[1]||(a[1]=(...u)=>e.onDragEnd&&e.onDragEnd(...u))},[t.createElementVNode("span",{class:"router-tab__item-title",title:e.getTabTitle(s)},[s.icon?(t.openBlock(),t.createElementBlock("i",{key:0,class:t.normalizeClass(["router-tab__item-icon",s.icon])},null,2)):t.createCommentVNode("",!0),t.createTextVNode(" "+t.toDisplayString(e.getTabTitle(s)),1)],8,Pe),e.isClosable(s)?(t.openBlock(),t.createElementBlock("a",{key:0,class:"router-tab__item-close",onClick:t.withModifiers(u=>e.close(s),["stop"])},null,8,Ee)):t.createCommentVNode("",!0)],42,Be))),128))]),_:1},16)]),t.createElementVNode("div",Ae,[t.renderSlot(e.$slots,"end")])]),t.createElementVNode("div",Ie,[t.createVNode(g,null,{default:t.withCtx(s=>[e.hasCustomSlot?t.renderSlot(e.$slots,"default",t.normalizeProps(t.mergeProps({key:0},{...s,controller:e.controller}))):(t.openBlock(),t.createElementBlock(t.Fragment,{key:1},[t.createVNode(t.Transition,t.mergeProps(e.pageTransitionProps,{appear:""}),{default:t.withCtx(()=>[e.controller.options.keepAlive?(t.openBlock(),t.createBlock(t.KeepAlive,{key:0,include:e.includeKeys,max:e.controller.options.maxAlive||void 0},[e.isRefreshing(s.route)?t.createCommentVNode("",!0):(t.openBlock(),t.createBlock(t.resolveDynamicComponent(s.Component),{key:e.controller.getRouteKey(s.route),class:"router-tab-page"}))],1032,["include","max"])):t.createCommentVNode("",!0)]),_:2},1040),t.createVNode(t.Transition,t.mergeProps(e.pageTransitionProps,{appear:""}),{default:t.withCtx(()=>[!e.controller.options.keepAlive||e.isRefreshing(s.route)?(t.openBlock(),t.createBlock(t.resolveDynamicComponent(s.Component),{key:e.controller.getRouteKey(s.route)+(e.isRefreshing(s.route)?"-refresh":""),class:"router-tab-page"})):t.createCommentVNode("",!0)]),_:2},1040)],64))]),_:3})]),e.context.visible&&e.context.target?(t.openBlock(),t.createElementBlock("div",{key:0,class:"router-tab__contextmenu",style:t.normalizeStyle({left:e.context.position.x+"px",top:e.context.position.y+"px"})},[(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(e.menuItems,s=>(t.openBlock(),t.createElementBlock("a",{key:s.id,class:"router-tab__contextmenu-item","aria-disabled":s.disabled,onClick:t.withModifiers(c=>e.handleMenuAction(s),["prevent"])},t.toDisplayString(s.label),9,xe))),128))],4)):t.createCommentVNode("",!0)])}const F=Te(ke,[["render",De]]),Se={class:"router-tabs","aria-hidden":"true"},_=t.defineComponent({name:"RouterTabs",__name:"RouterTabs",props:{cookieKey:{},expiresInDays:{},path:{},domain:{},secure:{type:Boolean},sameSite:{},serialize:{type:Function},deserialize:{type:Function},fallbackRoute:{}},setup(e){return U(e),(n,i)=>(t.openBlock(),t.createElementBlock("span",Se))}}),ae="tab-theme-style",Ke="tab-theme-primary-color",_e="system",$e="(prefers-color-scheme: dark)";let x=null;const k={primary:"#034960",background:"#ffffff",text:"#1e293b",border:"#e2e8f0",activeBackground:"#034960",activeText:"#ffffff",activeBorder:"#034960",headerBackground:"#ffff",buttonBackground:"#f8fafc",buttonColor:"#034960",activeButtonBackground:"#034960",activeButtonColor:"#ffffff",iconColor:"#475569"},Le={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 Y(e){typeof document>"u"||(console.log("applyPrimary",e),document.documentElement.style.setProperty("--router-tab-primary",e.primary??k.primary),document.documentElement.style.setProperty("--router-tab-header-bg",e.headerBackground??k.headerBackground),document.documentElement.style.setProperty("--router-tab-background",e.background??k.background),document.documentElement.style.setProperty("--router-tab-active-background",e.activeBackground??k.activeBackground),document.documentElement.style.setProperty("--router-tab-text",e.text??k.text),document.documentElement.style.setProperty("--router-tab-active-text",e.activeText??k.activeText),document.documentElement.style.setProperty("--router-tab-border",e.border??k.border),document.documentElement.style.setProperty("--router-tab-active-border",e.activeBorder??k.activeBorder),document.documentElement.style.setProperty("--router-tab-button-color",e.buttonColor??k.buttonColor),document.documentElement.style.setProperty("--router-tab-active-button-color",e.activeButtonColor??k.activeButtonColor),document.documentElement.style.setProperty("--router-tab-button-background",e.buttonBackground??k.buttonBackground),document.documentElement.style.setProperty("--router-tab-active-button-background",e.activeButtonBackground??k.activeButtonBackground))}function re(e){if(typeof document>"u")return;const a=document.documentElement,n=window.matchMedia($e),i=()=>{a.dataset.theme=n.matches?"dark":"light"};x&&(n.removeEventListener("change",x),x=null),e==="system"?(i(),x=()=>i(),n.addEventListener("change",x)):a.dataset.theme=e}function ie(e={}){if(typeof window>"u")return;const{styleKey:a=ae,defaultStyle:n=_e}=e,i=window.localStorage.getItem(a)??n;re(i),Y(i==="dark"?Le:k)}function Ve(e,a){if(typeof window>"u")return;const n=a?.styleKey??ae;window.localStorage.setItem(n,e),re(e)}function Me(e,a){if(typeof window>"u")return;const n=a?.primaryKey??Ke;window.localStorage.setItem(n,JSON.stringify(e)),Y(e)}const J={install(e){if(J._installed)return;J._installed=!0,ie();const a=F.name||"RouterTab",n=_.name||"RouterTabs";e.component(a,F),e.component(n,_),n!=="router-tabs"&&e.component("router-tabs",_),Object.defineProperty(e.config.globalProperties,"$tabs",{configurable:!0,enumerable:!1,get(){return e._context.provides[I]},set(i){i&&e.provide(I,i)}})}};y.RouterTab=F,y.RouterTabs=_,y.default=J,y.initRouterTabsTheme=ie,y.routerTabsKey=I,y.setRouterTabsPrimary=Me,y.setRouterTabsTheme=Ve,y.useRouterTabs=z,y.useRouterTabsPersistence=U,Object.defineProperties(y,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
package/index.d.ts CHANGED
@@ -13,7 +13,7 @@ import type {
13
13
  RouterTabsSnapshotTab,
14
14
  RouterTabsPersistenceOptions
15
15
  } from './lib/core/types'
16
- import type { RouterTabsThemeOptions } from './lib/theme'
16
+ import type { ColorStyle, RouterTabsThemeOptions } from './lib/theme'
17
17
 
18
18
  export type {
19
19
  TabRecord,
@@ -38,7 +38,7 @@ export declare function useRouterTabsPersistence(options?: RouterTabsPersistence
38
38
 
39
39
  export declare function initRouterTabsTheme(options?: RouterTabsThemeOptions): void
40
40
  export declare function setRouterTabsTheme(style: 'light' | 'dark' | 'system', options?: RouterTabsThemeOptions): void
41
- export declare function setRouterTabsPrimary(color: string, options?: RouterTabsThemeOptions): void
41
+ export declare function setRouterTabsPrimary(color: ColorStyle, options?: RouterTabsThemeOptions): void
42
42
 
43
43
  export declare const RouterTabs: DefineComponent<RouterTabsPersistenceOptions, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, Readonly<RouterTabsPersistenceOptions>, {}>
44
44
 
package/lib/theme.ts CHANGED
@@ -128,6 +128,7 @@ export function initRouterTabsTheme(options: RouterTabsThemeOptions = {}) {
128
128
  const storedStyle = (window.localStorage.getItem(styleKey) as 'light' | 'dark' | 'system' | null) ?? defaultStyle
129
129
 
130
130
  applyStyle(storedStyle)
131
+
131
132
  if (storedStyle === 'dark') {
132
133
  applyPrimary(defaultDarkColor)
133
134
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue3-router-tab",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",