Skip to content
This repository was archived by the owner on Apr 6, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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