Skip to content

Commit

Permalink
feat(nuxt3): support custom prop for <nuxt-link> (nuxt#4249)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Roe <daniel@roe.dev>
Co-authored-by: pooya parsa <pyapar@gmail.com>
  • Loading branch information
3 people authored Jul 7, 2022
1 parent c88e171 commit 38e0fa6
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 20 deletions.
1 change: 1 addition & 0 deletions docs/content/3.api/2.components/4.nuxt-link.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ In this example, we use `<NuxtLink>` with `target`, `rel`, and `noRel` props.
- **replace**: Works the same as [Vue Router's `replace` prop](https://router.vuejs.org/api/#replace) on internal links
- **ariaCurrentValue**: An `aria-current` attribute value to apply on exact active links. Works the same as [Vue Router's `aria-current-value` prop](https://router.vuejs.org/api/#aria-current-value) on internal links
- **external**: Forces the link to be considered as external (`true`) or internal (`false`). This is helpful to handle edge-cases
- **custom**: Whether `<NuxtLink>` should wrap its content in an `<a>` element. It allows taking full control of how a link is rendered and how navigation works when it is clicked. Works the same as [Vue Router's `custom` prop](https://router.vuejs.org/api/#custom)

::alert{icon=👉}
Defaults can be overwritten, see [overwriting defaults](#overwriting-defaults) if you want to change them.
Expand Down
8 changes: 8 additions & 0 deletions examples/routing/nuxt-link/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@
<NuxtLink to="/about">
About page
</NuxtLink>
<NuxtLink v-slot="{ navigate }" to="/about" custom>
<button @click="navigate">
Custom about page
</button>
</NuxtLink>
<NuxtLink to="https://nuxtjs.org">
Nuxt website
</NuxtLink>
<NuxtLink v-slot="{ href, target }" to="https://nuxtjs.org" custom>
<a :href="href" :target="target">Go to {{ href }}</a>
</NuxtLink>
<NuxtLink to="https://twitter.com/nuxt_js" target="_blank">
Nuxt Twitter with a blank target
</NuxtLink>
Expand Down
7 changes: 7 additions & 0 deletions examples/routing/universal-router/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ const timer = useState('timer', () => 0)
<NuxtLink to="/redirect" class="n-link-base">
Redirect
</NuxtLink>
<NuxtLink custom to="/redirect">
<template #default="{ href, navigate }">
<button @click="navigate">
Custom: {{ href }}
</button>
</template>
</NuxtLink>
</nav>
</template>

Expand Down
51 changes: 34 additions & 17 deletions packages/nuxt/src/app/components/nuxt-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,38 @@ import { defineComponent, h, resolveComponent, PropType, computed, DefineCompone
import { RouteLocationRaw, Router } from 'vue-router'
import { hasProtocol } from 'ufo'

import { useRouter } from '#app'
import { navigateTo, useRouter } from '#app'

const firstNonUndefined = <T>(...args: T[]): T => args.find(arg => arg !== undefined)

const DEFAULT_EXTERNAL_REL_ATTRIBUTE = 'noopener noreferrer'

export type NuxtLinkOptions = {
componentName?: string;
externalRelAttribute?: string | null;
activeClass?: string;
exactActiveClass?: string;
componentName?: string
externalRelAttribute?: string | null
activeClass?: string
exactActiveClass?: string
}

export type NuxtLinkProps = {
// Routing
to?: string | RouteLocationRaw;
href?: string | RouteLocationRaw;
external?: boolean;
to?: string | RouteLocationRaw
href?: string | RouteLocationRaw
external?: boolean
replace?: boolean
custom?: boolean

// Attributes
target?: string;
rel?: string;
noRel?: boolean;
target?: string
rel?: string
noRel?: boolean

// Styling
activeClass?: string;
exactActiveClass?: string;
activeClass?: string
exactActiveClass?: string

// Vue Router's `<RouterLink>` additional props
replace?: boolean;
ariaCurrentValue?: string;
ariaCurrentValue?: string
};

export function defineNuxtLink (options: NuxtLinkOptions) {
Expand Down Expand Up @@ -154,9 +155,9 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
activeClass: props.activeClass || options.activeClass,
exactActiveClass: props.exactActiveClass || options.exactActiveClass,
replace: props.replace,
ariaCurrentValue: props.ariaCurrentValue
ariaCurrentValue: props.ariaCurrentValue,
custom: props.custom
},
// TODO: Slot API
slots.default
)
}
Expand All @@ -175,6 +176,22 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
// converts `""` to `null` to prevent the attribute from being added as empty (`rel=""`)
: firstNonUndefined<string | null>(props.rel, options.externalRelAttribute, href ? DEFAULT_EXTERNAL_REL_ATTRIBUTE : '') || null

const navigate = () => navigateTo(href, { replace: props.replace })

// https://router.vuejs.org/api/#custom
if (props.custom) {
if (!slots.default) { return null }
return slots.default({
href,
navigate,
route: router.resolve(href),
rel,
target,
isActive: false,
isExactActive: false
})
}

return h('a', { href, rel, target }, slots.default?.())
}
}
Expand Down
20 changes: 18 additions & 2 deletions packages/nuxt/src/app/plugins/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,24 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {

nuxtApp.vueApp.component('RouterLink', {
functional: true,
props: { to: String },
setup: (props, { slots }) => () => h('a', { href: props.to, onClick: (e) => { e.preventDefault(); router.push(props.to) } }, slots)
props: {
to: String,
custom: Boolean,
replace: Boolean,
// Not implemented
activeClass: String,
exactActiveClass: String,
ariaCurrentValue: String
},
setup: (props, { slots }) => {
const navigate = () => handleNavigation(props.to, props.replace)
return () => {
const route = router.resolve(props.to)
return props.custom
? slots.default?.({ href: props.to, navigate, route })
: h('a', { href: props.to, onClick: (e) => { e.preventDefault(); return navigate() } }, slots)
}
}
})

if (process.client) {
Expand Down
5 changes: 4 additions & 1 deletion packages/nuxt/src/pages/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ export default defineNuxtModule({
}

nuxt.hook('autoImports:extend', (autoImports) => {
autoImports.push({ name: 'definePageMeta', as: 'definePageMeta', from: resolve(runtimeDir, 'composables') })
autoImports.push(
{ name: 'definePageMeta', as: 'definePageMeta', from: resolve(runtimeDir, 'composables') },
{ name: 'useLink', as: 'useLink', from: 'vue-router' }
)
})

// Extract macros from pages
Expand Down

0 comments on commit 38e0fa6

Please sign in to comment.