@tanstack/vue-router
Version:
Modern and scalable routing for Vue applications
165 lines • 6.23 kB
JSX
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