]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat: type useLink
authorEduardo San Martin Morote <posva13@gmail.com>
Wed, 12 Jun 2024 08:34:13 +0000 (10:34 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Wed, 12 Jun 2024 08:34:13 +0000 (10:34 +0200)
packages/playground/src/App.vue
packages/router/src/RouterLink.ts
packages/router/src/index.ts
packages/router/src/router.ts
packages/router/src/typed-routes/route-location.ts

index a388935568f29eddbf37687124eb78788d5b59ed..170d73919e8393e7855e4390172d8722703aa301 100644 (file)
 </template>
 
 <script lang="ts" setup>
-import { defineComponent, inject, computed, ref } from 'vue'
+import { inject, computed, ref } from 'vue'
 import { scrollWaiter } from './scrollWaiter'
 import { useLink, useRoute, RouterLink } from 'vue-router'
 import AppLink from './AppLink.vue'
@@ -213,13 +213,7 @@ const nextUserLink = computed(
   () => '/users/' + String((Number(route.params.id) || 0) + 1)
 )
 
-currentLocation,
-  nextUserLink,
-  state,
-  flushWaiter,
-  setupWaiter,
-  viewName,
-  function toggleViewName() {
-    viewName.value = viewName.value === 'default' ? 'other' : 'default'
-  }
+function toggleViewName() {
+  viewName.value = viewName.value === 'default' ? 'other' : 'default'
+}
 </script>
index 9531ba65bc209593c1b58d7ebc1cff44bdf9fee9..731dfbf7502d57977a8af88f85b34fb7f2943513 100644 (file)
@@ -25,6 +25,7 @@ import {
   RendererNode,
   // @ts-ignore
   ComponentOptionsMixin,
+  MaybeRef,
 } from 'vue'
 import { isSameRouteLocationParams, isSameRouteRecord } from './location'
 import { routerKey, routeLocationKey } from './injectionSymbols'
@@ -32,11 +33,15 @@ import { RouteRecord } from './matcher/types'
 import { NavigationFailure } from './errors'
 import { isArray, isBrowser, noop } from './utils'
 import { warn } from './warning'
-import { isRouteLocation, type VueUseOptions } from './types'
+import { isRouteLocation } from './types'
 import {
   RouteLocation,
+  RouteLocationAsPath,
+  RouteLocationAsRelativeTyped,
+  RouteLocationAsString,
   RouteLocationRaw,
   RouteLocationResolved,
+  RouteMap,
 } from './typed-routes'
 
 export interface RouterLinkOptions {
@@ -80,6 +85,10 @@ export interface RouterLinkProps extends RouterLinkOptions {
     | 'false'
 }
 
+/**
+ * Context passed from router-link components to devtools.
+ * @internal
+ */
 export interface UseLinkDevtoolsContext {
   route: RouteLocationResolved
   isActive: boolean
@@ -87,11 +96,41 @@ export interface UseLinkDevtoolsContext {
   error: string | null
 }
 
-export type UseLinkOptions = VueUseOptions<RouterLinkOptions>
+/**
+ * Options passed to {@link useLink}.
+ */
+export interface UseLinkOptions<Name extends keyof RouteMap = keyof RouteMap> {
+  to: MaybeRef<
+    | RouteLocationAsString
+    | RouteLocationAsRelativeTyped<RouteMap, Name>
+    | RouteLocationAsPath
+    | RouteLocationRaw
+  >
+  replace?: MaybeRef<boolean | undefined>
+}
+
+/**
+ * Return type of {@link useLink}.
+ * @internal
+ */
+export interface UseLinkReturn<Name extends keyof RouteMap = keyof RouteMap> {
+  route: ComputedRef<RouteLocationResolved<Name>>
+  href: ComputedRef<string>
+  isActive: ComputedRef<boolean>
+  isExactActive: ComputedRef<boolean>
+  navigate(e?: MouseEvent): Promise<void | NavigationFailure>
+}
 
 // TODO: we could allow currentRoute as a prop to expose `isActive` and
 // `isExactActive` behavior should go through an RFC
-export function useLink(props: UseLinkOptions) {
+/**
+ * Returns the internal behavior of a {@link RouterLink} without the rendering part.
+ *
+ * @param props - a `to` location and an optional `replace` flag
+ */
+export function useLink<Name extends keyof RouteMap = keyof RouteMap>(
+  props: UseLinkOptions<Name>
+): UseLinkReturn<Name> {
   const router = inject(routerKey)!
   const currentRoute = inject(routeLocationKey)!
 
@@ -316,7 +355,8 @@ export interface _RouterLinkI {
         isActive,
         isExactActive,
         navigate,
-      }: UnwrapRef<ReturnType<typeof useLink>>) => VNode[]
+      }: // TODO: How do we add the name generic
+      UnwrapRef<UseLinkReturn>) => VNode[]
     }
   }
 
index 058feebae4187afb33cbef97519e7f258c247bbe..7e1464b35248623a719e0e9c767b8a6ccd04f1f9 100644 (file)
@@ -75,9 +75,9 @@ export type {
   // route location
   RouteLocationRaw,
   RouteLocation,
+  RouteLocationGeneric,
   RouteLocationTyped,
   RouteLocationTypedList,
-  RouteLocationGeneric,
 
   // RouteLocationNormalized
   RouteLocationNormalizedGeneric,
@@ -92,20 +92,23 @@ export type {
   RouteLocationNormalizedLoadedTypedList,
 
   // RouteLocationResolved
-  RouteLocationResolved,
   RouteLocationResolvedGeneric,
+  RouteLocationResolved,
   RouteLocationResolvedTyped,
   RouteLocationResolvedTypedList,
 
   // relative
-  RouteLocationAsRelative,
   RouteLocationAsRelativeGeneric,
+  RouteLocationAsRelative,
   RouteLocationAsRelativeTyped,
   RouteLocationAsRelativeTypedList,
   // string
+  RouteLocationAsStringTyped,
   RouteLocationAsString,
+  RouteLocationAsStringTypedList,
   // as path
   RouteLocationAsPathGeneric,
+  RouteLocationAsPath,
   RouteLocationAsPathTyped,
   RouteLocationAsPathTypedList,
 
index c5a323727935dd8366fff03412defbeda7eaa5f7..b54e0ceced6d25c65998e5ac4784eec419c6c864 100644 (file)
@@ -16,6 +16,9 @@ import type {
   NavigationGuardWithThis,
   NavigationHookAfter,
   RouteLocationResolved,
+  RouteLocationAsRelative,
+  RouteLocationAsPath,
+  RouteLocationAsString,
 } from './typed-routes'
 import { RouterHistory, HistoryState, NavigationType } from './history/common'
 import {
@@ -64,11 +67,7 @@ import {
 } from './injectionSymbols'
 import { addDevtools } from './devtools'
 import { _LiteralUnion } from './types/utils'
-import {
-  RouteLocationAsPathTyped,
-  RouteLocationAsRelativeTyped,
-  RouteLocationAsString,
-} from './typed-routes/route-location'
+import { RouteLocationAsRelativeTyped } from './typed-routes/route-location'
 import { RouteMap } from './typed-routes/route-map'
 
 /**
@@ -249,12 +248,16 @@ export interface Router {
    * @param currentLocation - Optional current location to resolve against
    */
   resolve<Name extends keyof RouteMap = keyof RouteMap>(
-    to:
-      | RouteLocationAsString<RouteMap>
-      | RouteLocationAsRelativeTyped<RouteMap, Name>
-      | RouteLocationAsPathTyped<RouteMap, Name>,
+    to: RouteLocationAsRelativeTyped<RouteMap, Name>,
+    // NOTE: This version doesn't work probably because it infers the type too early
+    // | RouteLocationAsRelative<Name>
     currentLocation?: RouteLocationNormalizedLoaded
   ): RouteLocationResolved<Name>
+  resolve(
+    // not having the overload produces errors in RouterLink calls to router.resolve()
+    to: RouteLocationAsString | RouteLocationAsRelative | RouteLocationAsPath,
+    currentLocation?: RouteLocationNormalizedLoaded
+  ): RouteLocationResolved
 
   /**
    * Programmatically navigate to a new URL by pushing an entry in the history
index 6cf08cee1ecf3fb6d0e97486e3093aa9b915cec9..2807afcbdf4cc4fe76c0f7fb9dd14e25aa50256f 100644 (file)
@@ -34,7 +34,6 @@ export interface RouteLocationGeneric extends _RouteLocationBase {
 
 /**
  * Helper to generate a type safe version of the {@link RouteLocation} type.
- * @internal
  */
 export interface RouteLocationTyped<
   RouteMap extends RouteMapGeneric,
@@ -67,7 +66,6 @@ export interface RouteLocationNormalizedGeneric extends _RouteLocationBase {
 
 /**
  * Helper to generate a type safe version of the {@link RouteLocationNormalized} type.
- * @internal
  */
 export interface RouteLocationNormalizedTyped<
   RouteMap extends RouteMapGeneric = RouteMapGeneric,
@@ -107,7 +105,6 @@ export interface RouteLocationNormalizedLoadedGeneric
 
 /**
  * Helper to generate a type safe version of the {@link RouteLocationNormalizedLoaded} type.
- * @internal
  */
 export interface RouteLocationNormalizedLoadedTyped<
   RouteMap extends RouteMapGeneric = RouteMapGeneric,
@@ -142,7 +139,6 @@ export interface RouteLocationAsRelativeGeneric
 
 /**
  * Helper to generate a type safe version of the {@link RouteLocationAsRelative} type.
- * @internal
  */
 export interface RouteLocationAsRelativeTyped<
   RouteMap extends RouteMapGeneric = RouteMapGeneric,
@@ -174,7 +170,6 @@ export interface RouteLocationAsPathGeneric
 
 /**
  * Helper to generate a type safe version of the {@link RouteLocationAsPath} type.
- * @internal
  */
 export interface RouteLocationAsPathTyped<
   RouteMap extends RouteMapGeneric = RouteMapGeneric,
@@ -187,13 +182,29 @@ export interface RouteLocationAsPathTyped<
 }
 
 /**
- * Type safe version to auto complete the path of a route.
+ * List of all possible {@link RouteLocationAsPath} indexed by the route name.
  * @internal
  */
 export type RouteLocationAsPathTypedList<
   RouteMap extends RouteMapGeneric = RouteMapGeneric
 > = { [N in keyof RouteMap]: RouteLocationAsPathTyped<RouteMap, N> }
 
+/**
+ * Helper to generate a type safe version of the {@link RouteLocationAsString} type.
+ */
+export type RouteLocationAsStringTyped<
+  RouteMap extends RouteMapGeneric = RouteMapGeneric,
+  Name extends keyof RouteMap = keyof RouteMap
+> = RouteMap[Name]['path']
+
+/**
+ * List of all possible {@link RouteLocationAsString} indexed by the route name.
+ * @internal
+ */
+export type RouteLocationAsStringTypedList<
+  RouteMap extends RouteMapGeneric = RouteMapGeneric
+> = { [N in keyof RouteMap]: RouteLocationAsStringTyped<RouteMap, N> }
+
 /**
  * Generic version of {@link RouteLocationResolved}. It is used when no {@link RouteMap} is provided.
  */
@@ -206,7 +217,6 @@ export interface RouteLocationResolvedGeneric extends RouteLocationGeneric {
 
 /**
  * Helper to generate a type safe version of the {@link RouteLocationResolved} type.
- * @internal
  */
 export interface RouteLocationResolvedTyped<
   RouteMap extends RouteMapGeneric,
@@ -278,14 +288,21 @@ export type RouteLocationResolved<
   : RouteLocationResolvedTypedList<RouteMap>[Name]
 
 /**
- * Same as {@link RouteLocationAsPathTyped} but as a string literal.
- * @internal
+ * Same as {@link RouteLocationAsPath} but as a string literal.
  */
 export type RouteLocationAsString<
-  RouteMap extends RouteMapGeneric = RouteMapGeneric
+  Name extends keyof RouteMap = keyof RouteMap
 > = RouteMapGeneric extends RouteMap
   ? string
-  : _LiteralUnion<RouteMap[keyof RouteMap]['path'], string>
+  : _LiteralUnion<RouteLocationAsStringTypedList<RouteMap>[Name], string>
+
+/**
+ * Route location as an object with a `path` property.
+ */
+export type RouteLocationAsPath<Name extends keyof RouteMap = keyof RouteMap> =
+  RouteMapGeneric extends RouteMap
+    ? RouteLocationAsPathGeneric
+    : RouteLocationAsPathTypedList<RouteMap>[Name]
 
 /**
  * Route location that can be passed to `router.push()` and other user-facing APIs.
@@ -293,10 +310,10 @@ export type RouteLocationAsString<
 export type RouteLocationRaw<Name extends keyof RouteMap = keyof RouteMap> =
   RouteMapGeneric extends RouteMap
     ?
-        | RouteLocationAsString
+        | RouteLocationAsStringTyped
         | RouteLocationAsRelativeGeneric
         | RouteLocationAsPathGeneric
     :
-        | RouteLocationAsString<RouteMap>
+        | RouteLocationAsStringTyped<RouteMap>
         | RouteLocationAsRelativeTypedList<RouteMap>[Name]
         | RouteLocationAsPathTypedList<RouteMap>[Name]