UNPKG

@tanstack/vue-router

Version:

Modern and scalable routing for Vue applications

165 lines 6.23 kB
import * as Vue from 'vue'; import { isServer } from '@tanstack/router-core/isServer'; import { useStore } from '@tanstack/vue-store'; import { CatchBoundary } from './CatchBoundary'; import { useRouter } from './useRouter'; import { useTransitionerSetup } from './Transitioner'; import { routeIdContext } from './matchContext'; import { Match } from './Match'; import { renderInNonRouteComponentContext } from './nonRouteComponentContext'; export const Matches = Vue.defineComponent({ name: 'Matches', setup() { const router = useRouter(); useTransitionerSetup(); return () => { const pendingElement = router.options.defaultPendingComponent ? process.env.NODE_ENV !== 'production' ? renderInNonRouteComponentContext(router.options.defaultPendingComponent, undefined, 'pendingComponent') : Vue.h(router.options.defaultPendingComponent) : null; // Do not render a root Suspense during SSR or hydrating from SSR const inner = (isServer ?? router.isServer) || (typeof document !== 'undefined' && router.ssr) ? Vue.h(MatchesInner) : Vue.h(Vue.Suspense, { fallback: pendingElement }, { default: () => Vue.h(MatchesInner), }); return router.options.InnerWrap ? Vue.h(router.options.InnerWrap, null, { default: () => inner }) : inner; }; }, }); // Create a simple error component function that matches ErrorRouteComponent const errorComponentFn = (props) => { return Vue.h('div', { class: 'error' }, [ Vue.h('h1', null, 'Error'), Vue.h('p', null, props.error.message || String(props.error)), Vue.h('button', { onClick: props.reset }, 'Try Again'), ]); }; const MatchesInner = Vue.defineComponent({ name: 'MatchesInner', setup() { const router = useRouter(); const matches = useStore(router.stores.matches); const routeId = Vue.computed(() => matches.value[0]?.routeId); return () => { // Generate a placeholder element if routeId.value is not present const childElement = routeId.value ? Vue.h(Match, { routeId: routeId.value }) : Vue.h('div'); // If disableGlobalCatchBoundary is true, don't wrap in CatchBoundary if (router.options.disableGlobalCatchBoundary) { return childElement; } return Vue.h(CatchBoundary, { getResetKey: () => matches.value, errorComponent: errorComponentFn, onCatch: process.env.NODE_ENV !== 'production' ? (error) => { console.warn(`Warning: The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!`); console.warn(`Warning: ${error.message || error.toString()}`); } : undefined, children: childElement, }); }; }, }); export function useMatchRoute() { const router = useRouter(); const location = useStore(router.stores.location, (value) => value.href); const resolvedLocation = useStore(router.stores.resolvedLocation, (value) => value?.href); const status = useStore(router.stores.status); return (opts) => { const matchRoute = Vue.computed(() => { location.value; resolvedLocation.value; status.value; const { pending, caseSensitive, fuzzy, includeSearch, ...rest } = opts; return router.matchRoute(rest, { pending, caseSensitive, fuzzy, includeSearch, }); }); return matchRoute; }; } export const MatchRoute = Vue.defineComponent({ name: 'MatchRoute', props: { // Define props to match MakeMatchRouteOptions from: { type: String, required: false, }, to: { type: String, required: false, }, fuzzy: { type: Boolean, required: false, }, caseSensitive: { type: Boolean, required: false, }, includeSearch: { type: Boolean, required: false, }, pending: { type: Boolean, required: false, }, }, setup(props, { slots }) { const params = useMatchRoute()(props); return () => { const match = params.value; // Create a component that renders the slot in a reactive manner if (!match || !slots.default) { return null; } // For function slots, pass the params if (typeof slots.default === 'function') { // Use h to create a wrapper component that will call the slot function return Vue.h(Vue.Fragment, null, slots.default(match)); } // For normal slots, just render them return Vue.h(Vue.Fragment, null, slots.default); }; }, }); export function useMatches(opts) { const router = useRouter(); return useStore(router.stores.matches, (matches) => { return opts?.select ? opts.select(matches) : matches; }); } export function useParentMatches(opts) { const contextRouteId = Vue.inject(routeIdContext); return useMatches({ select: (matches) => { matches = matches.slice(0, matches.findIndex((d) => d.routeId === contextRouteId)); return opts?.select ? opts.select(matches) : matches; }, }); } export function useChildMatches(opts) { const contextRouteId = Vue.inject(routeIdContext); return useMatches({ select: (matches) => { matches = matches.slice(matches.findIndex((d) => d.routeId === contextRouteId) + 1); return opts?.select ? opts.select(matches) : matches; }, }); } //# sourceMappingURL=Matches.jsx.map