|
| 1 | +import type { Ref } from "vue"; |
| 2 | +import type { LocationQueryRaw, RouteLocationNormalizedLoaded, RouteRecordRaw } from "vue-router"; |
| 3 | + |
| 4 | +import { computed, defineComponent, h } from "vue"; |
| 5 | + |
| 6 | +import type { RouteName, RouterLinkComponent, RouterService, RouterViewComponent } from "./types"; |
| 7 | + |
| 8 | +const buildRouteKey = (route: RouteLocationNormalizedLoaded, depth: number): string => { |
| 9 | + let key = route.matched[depth].path; |
| 10 | + for (const [paramName, paramValue] of Object.entries(route.params)) { |
| 11 | + const value = Array.isArray(paramValue) ? paramValue[0] : paramValue; |
| 12 | + if (value) key = key.replace(`:${paramName}`, value); |
| 13 | + } |
| 14 | + |
| 15 | + return key; |
| 16 | +}; |
| 17 | + |
| 18 | +export const createRouterView = ( |
| 19 | + currentRouteRef: Ref<RouteLocationNormalizedLoaded>, |
| 20 | +): RouterViewComponent => |
| 21 | + defineComponent<{ depth?: number }>( |
| 22 | + ({ depth = 0 }) => { |
| 23 | + const component = computed(() => { |
| 24 | + const matched = currentRouteRef.value.matched[depth]; |
| 25 | + return matched?.components?.default ?? null; |
| 26 | + }); |
| 27 | + |
| 28 | + return () => { |
| 29 | + if (!component.value) return h("p", ["404"]); |
| 30 | + |
| 31 | + return h(component.value, { |
| 32 | + key: buildRouteKey(currentRouteRef.value, depth), |
| 33 | + }); |
| 34 | + }; |
| 35 | + }, |
| 36 | + // https://vuejs.org/api/general.html#function-signature |
| 37 | + // manual runtime props declaration is currently still needed |
| 38 | + { props: ["depth"] }, |
| 39 | + ); |
| 40 | + |
| 41 | +export const createRouterLink = <Routes extends RouteRecordRaw[]>( |
| 42 | + getUrlForRouteName: RouterService<Routes>["getUrlForRouteName"], |
| 43 | + goToRoute: RouterService<Routes>["goToRoute"], |
| 44 | +): RouterLinkComponent<Routes> => |
| 45 | + defineComponent<{ |
| 46 | + to: { |
| 47 | + name: RouteName<Routes>; |
| 48 | + query?: LocationQueryRaw; |
| 49 | + id?: number | string; |
| 50 | + parentId?: number; |
| 51 | + }; |
| 52 | + }>( |
| 53 | + (props, { slots }) => |
| 54 | + () => |
| 55 | + h( |
| 56 | + "a", |
| 57 | + { |
| 58 | + href: getUrlForRouteName(props.to.name, props.to.id, props.to.query, props.to.parentId), |
| 59 | + onClick: (event: MouseEvent) => { |
| 60 | + if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return; |
| 61 | + |
| 62 | + event.preventDefault(); |
| 63 | + goToRoute(props.to.name, props.to.id, props.to.query, props.to.parentId); |
| 64 | + }, |
| 65 | + }, |
| 66 | + slots.default?.(), |
| 67 | + ), |
| 68 | + // https://vuejs.org/api/general.html#function-signature |
| 69 | + // manual runtime props declaration is currently still needed |
| 70 | + { props: ["to"] }, |
| 71 | + ); |
0 commit comments