UNPKG

@esmx/router-vue

Version:

Vue integration for @esmx/router - A universal router that works seamlessly with both Vue 2.7+ and Vue 3

115 lines (114 loc) 3.2 kB
import { computed, getCurrentInstance, inject, onBeforeUnmount, provide, ref } from "vue"; import { createDependentProxy, createSymbolProperty } from "./util.mjs"; const ROUTER_CONTEXT_KEY = Symbol("router-context"); const ROUTER_INJECT_KEY = Symbol("router-inject"); const ROUTER_VIEW_DEPTH_KEY = Symbol("router-view-depth"); const routerContextProperty = createSymbolProperty(ROUTER_CONTEXT_KEY); const routerViewDepthProperty = createSymbolProperty( ROUTER_VIEW_DEPTH_KEY ); function getCurrentProxy() { const instance = getCurrentInstance(); if (!instance || !instance.proxy) { throw new Error( "[@esmx/router-vue] Must be used within setup() or other composition functions" ); } return instance.proxy; } function findRouterContext(vm) { if (!vm) { vm = getCurrentProxy(); } let context = routerContextProperty.get(vm); if (context) { return context; } let current = vm.$parent; while (current) { context = routerContextProperty.get(current); if (context) { routerContextProperty.set(vm, context); return context; } current = current.$parent; } throw new Error( "[@esmx/router-vue] Router context not found. Please ensure useProvideRouter() is called in a parent component." ); } export function getRouter(instance) { return findRouterContext(instance).router; } export function getRoute(instance) { return findRouterContext(instance).route; } function useRouterContext() { const injectedContext = inject(ROUTER_INJECT_KEY); if (injectedContext) { return injectedContext; } const proxy = getCurrentProxy(); return findRouterContext(proxy); } export function useRouter() { return useRouterContext().router; } export function useRoute() { return useRouterContext().route; } export function useProvideRouter(router) { const proxy = getCurrentProxy(); const dep = ref(0); const proxiedRouter = createDependentProxy(router, dep); const proxiedRoute = createDependentProxy(router.route, dep); const context = { router: proxiedRouter, route: proxiedRoute }; provide(ROUTER_INJECT_KEY, context); routerContextProperty.set(proxy, context); const unwatch = router.afterEach((to) => { if (router.route === to) { to.syncTo(proxiedRoute); dep.value++; } }); onBeforeUnmount(unwatch); } export function _useRouterViewDepth(isRender) { const depth = inject(ROUTER_VIEW_DEPTH_KEY, 0); if (isRender) { provide(ROUTER_VIEW_DEPTH_KEY, depth + 1); const proxy = getCurrentProxy(); routerViewDepthProperty.set(proxy, depth + 1); } return depth; } export function useRouterViewDepth() { return _useRouterViewDepth(); } export function getRouterViewDepth(instance) { let current = instance.$parent; while (current) { const value = routerViewDepthProperty.get(current); if (typeof value === "number") return value; current = current.$parent; } throw new Error( "[@esmx/router-vue] RouterView depth not found. Please ensure a RouterView exists in ancestor components." ); } export function useLink(props) { const router = useRouter(); return computed(() => { return router.resolveLink(props); }); }